blob: 886eee0062e0e8410a94b3a5b80373f9a90c468e [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;
Namhyung Kim112f7612014-04-22 14:05:35 +090029 u64 nr_non_filtered_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);
Namhyung Kim112f7612014-04-22 14:05:35 +090036static void hist_browser__update_nr_entries(struct hist_browser *hb);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -030037
Namhyung Kim268397c2014-04-22 14:49:31 +090038static bool hist_browser__has_filter(struct hist_browser *hb)
39{
40 return hists__has_filter(hb->hists) || hb->min_pcnt;
41}
42
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030043static void hist_browser__refresh_dimensions(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030044{
45 /* 3 == +/- toggle symbol before actual hist_entry rendering */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030046 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030047 sizeof("[k]"));
48}
49
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030050static void hist_browser__reset(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030051{
Namhyung Kim268397c2014-04-22 14:49:31 +090052 hist_browser__update_nr_entries(browser);
53 browser->b.nr_entries = browser->nr_non_filtered_entries;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030054 hist_browser__refresh_dimensions(browser);
55 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030056}
57
58static char tree__folded_sign(bool unfolded)
59{
60 return unfolded ? '-' : '+';
61}
62
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030063static char map_symbol__folded(const struct map_symbol *ms)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030064{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030065 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030066}
67
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030068static char hist_entry__folded(const struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030069{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030070 return map_symbol__folded(&he->ms);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030071}
72
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030073static char callchain_list__folded(const struct callchain_list *cl)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030074{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030075 return map_symbol__folded(&cl->ms);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030076}
77
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030078static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -030079{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030080 ms->unfolded = unfold ? ms->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -030081}
82
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030083static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030084{
85 int n = 0;
86 struct rb_node *nd;
87
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030088 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030089 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
90 struct callchain_list *chain;
91 char folded_sign = ' '; /* No children */
92
93 list_for_each_entry(chain, &child->val, list) {
94 ++n;
95 /* We need this because we may not have children */
96 folded_sign = callchain_list__folded(chain);
97 if (folded_sign == '+')
98 break;
99 }
100
101 if (folded_sign == '-') /* Have children and they're unfolded */
102 n += callchain_node__count_rows_rb_tree(child);
103 }
104
105 return n;
106}
107
108static int callchain_node__count_rows(struct callchain_node *node)
109{
110 struct callchain_list *chain;
111 bool unfolded = false;
112 int n = 0;
113
114 list_for_each_entry(chain, &node->val, list) {
115 ++n;
116 unfolded = chain->ms.unfolded;
117 }
118
119 if (unfolded)
120 n += callchain_node__count_rows_rb_tree(node);
121
122 return n;
123}
124
125static int callchain__count_rows(struct rb_root *chain)
126{
127 struct rb_node *nd;
128 int n = 0;
129
130 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
131 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
132 n += callchain_node__count_rows(node);
133 }
134
135 return n;
136}
137
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300138static bool map_symbol__toggle_fold(struct map_symbol *ms)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300139{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300140 if (!ms)
Jiri Olsa8493fe12012-04-04 22:21:31 +0200141 return false;
142
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300143 if (!ms->has_children)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300144 return false;
145
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300146 ms->unfolded = !ms->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300147 return true;
148}
149
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300150static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300151{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300152 struct rb_node *nd = rb_first(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300153
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300154 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300155 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
156 struct callchain_list *chain;
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300157 bool first = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300158
159 list_for_each_entry(chain, &child->val, list) {
160 if (first) {
161 first = false;
162 chain->ms.has_children = chain->list.next != &child->val ||
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300163 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300164 } else
165 chain->ms.has_children = chain->list.next == &child->val &&
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300166 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300167 }
168
169 callchain_node__init_have_children_rb_tree(child);
170 }
171}
172
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300173static void callchain_node__init_have_children(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300174{
175 struct callchain_list *chain;
176
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300177 list_for_each_entry(chain, &node->val, list)
178 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300179
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300180 callchain_node__init_have_children_rb_tree(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300181}
182
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300183static void callchain__init_have_children(struct rb_root *root)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300184{
185 struct rb_node *nd;
186
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300187 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300188 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
189 callchain_node__init_have_children(node);
190 }
191}
192
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300193static void hist_entry__init_have_children(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300194{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300195 if (!he->init_have_children) {
196 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
197 callchain__init_have_children(&he->sorted_chain);
198 he->init_have_children = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300199 }
200}
201
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300202static bool hist_browser__toggle_fold(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300203{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300204 if (map_symbol__toggle_fold(browser->selection)) {
205 struct hist_entry *he = browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300206
207 hist_entry__init_have_children(he);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300208 browser->hists->nr_entries -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300209
210 if (he->ms.unfolded)
211 he->nr_rows = callchain__count_rows(&he->sorted_chain);
212 else
213 he->nr_rows = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300214 browser->hists->nr_entries += he->nr_rows;
215 browser->b.nr_entries = browser->hists->nr_entries;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300216
217 return true;
218 }
219
220 /* If it doesn't have children, no toggling performed */
221 return false;
222}
223
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300224static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300225{
226 int n = 0;
227 struct rb_node *nd;
228
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300229 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300230 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
231 struct callchain_list *chain;
232 bool has_children = false;
233
234 list_for_each_entry(chain, &child->val, list) {
235 ++n;
236 map_symbol__set_folding(&chain->ms, unfold);
237 has_children = chain->ms.has_children;
238 }
239
240 if (has_children)
241 n += callchain_node__set_folding_rb_tree(child, unfold);
242 }
243
244 return n;
245}
246
247static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
248{
249 struct callchain_list *chain;
250 bool has_children = false;
251 int n = 0;
252
253 list_for_each_entry(chain, &node->val, list) {
254 ++n;
255 map_symbol__set_folding(&chain->ms, unfold);
256 has_children = chain->ms.has_children;
257 }
258
259 if (has_children)
260 n += callchain_node__set_folding_rb_tree(node, unfold);
261
262 return n;
263}
264
265static int callchain__set_folding(struct rb_root *chain, bool unfold)
266{
267 struct rb_node *nd;
268 int n = 0;
269
270 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
271 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
272 n += callchain_node__set_folding(node, unfold);
273 }
274
275 return n;
276}
277
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300278static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300279{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300280 hist_entry__init_have_children(he);
281 map_symbol__set_folding(&he->ms, unfold);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300282
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300283 if (he->ms.has_children) {
284 int n = callchain__set_folding(&he->sorted_chain, unfold);
285 he->nr_rows = unfold ? n : 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300286 } else
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300287 he->nr_rows = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300288}
289
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300290static void hists__set_folding(struct hists *hists, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300291{
292 struct rb_node *nd;
293
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300294 hists->nr_entries = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300295
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300296 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300297 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
298 hist_entry__set_folding(he, unfold);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300299 hists->nr_entries += 1 + he->nr_rows;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300300 }
301}
302
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300303static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300304{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300305 hists__set_folding(browser->hists, unfold);
306 browser->b.nr_entries = browser->hists->nr_entries;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300307 /* Go to the start, we may be way after valid entries after a collapse */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300308 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300309}
310
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200311static void ui_browser__warn_lost_events(struct ui_browser *browser)
312{
313 ui_browser__warning(browser, 4,
314 "Events are being lost, check IO/CPU overload!\n\n"
315 "You may want to run 'perf' using a RT scheduler policy:\n\n"
316 " perf top -r 80\n\n"
317 "Or reduce the sampling frequency.");
318}
319
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300320static int hist_browser__run(struct hist_browser *browser, const char *ev_name,
Namhyung Kim9783adf2012-11-02 14:50:05 +0900321 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300322{
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300323 int key;
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300324 char title[160];
Namhyung Kim9783adf2012-11-02 14:50:05 +0900325 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300326
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300327 browser->b.entries = &browser->hists->entries;
Namhyung Kim268397c2014-04-22 14:49:31 +0900328 if (hist_browser__has_filter(browser))
Namhyung Kim112f7612014-04-22 14:05:35 +0900329 browser->b.nr_entries = browser->nr_non_filtered_entries;
Namhyung Kim268397c2014-04-22 14:49:31 +0900330 else
331 browser->b.nr_entries = browser->hists->nr_entries;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300332
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300333 hist_browser__refresh_dimensions(browser);
334 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300335
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300336 if (ui_browser__show(&browser->b, title,
Arnaldo Carvalho de Melo59e8fe32010-08-10 15:44:20 -0300337 "Press '?' for help on key bindings") < 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300338 return -1;
339
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300340 while (1) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300341 key = ui_browser__run(&browser->b, delay_secs);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300342
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300343 switch (key) {
Namhyung Kimfa5df942013-05-14 11:09:05 +0900344 case K_TIMER: {
345 u64 nr_entries;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900346 hbt->timer(hbt->arg);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900347
Namhyung Kim268397c2014-04-22 14:49:31 +0900348 if (hist_browser__has_filter(browser)) {
Namhyung Kim112f7612014-04-22 14:05:35 +0900349 hist_browser__update_nr_entries(browser);
350 nr_entries = browser->nr_non_filtered_entries;
Namhyung Kimfa5df942013-05-14 11:09:05 +0900351 } else {
352 nr_entries = browser->hists->nr_entries;
353 }
354
355 ui_browser__update_nr_entries(&browser->b, nr_entries);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200356
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300357 if (browser->hists->stats.nr_lost_warned !=
358 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
359 browser->hists->stats.nr_lost_warned =
360 browser->hists->stats.nr_events[PERF_RECORD_LOST];
361 ui_browser__warn_lost_events(&browser->b);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200362 }
363
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300364 hists__browser_title(browser->hists, title, sizeof(title), ev_name);
365 ui_browser__show_title(&browser->b, title);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300366 continue;
Namhyung Kimfa5df942013-05-14 11:09:05 +0900367 }
Arnaldo Carvalho de Melo46941532010-08-10 15:50:07 -0300368 case 'D': { /* Debug */
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300369 static int seq;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300370 struct hist_entry *h = rb_entry(browser->b.top,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300371 struct hist_entry, rb_node);
372 ui_helpline__pop();
373 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 -0300374 seq++, browser->b.nr_entries,
375 browser->hists->nr_entries,
376 browser->b.height,
377 browser->b.index,
378 browser->b.top_idx,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300379 h->row_offset, h->nr_rows);
380 }
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300381 break;
382 case 'C':
383 /* Collapse the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300384 hist_browser__set_folding(browser, false);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300385 break;
386 case 'E':
387 /* Expand the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300388 hist_browser__set_folding(browser, true);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300389 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -0200390 case K_ENTER:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300391 if (hist_browser__toggle_fold(browser))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300392 break;
393 /* fall thru */
394 default:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300395 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300396 }
397 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300398out:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300399 ui_browser__hide(&browser->b);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300400 return key;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300401}
402
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300403static char *callchain_list__sym_name(struct callchain_list *cl,
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300404 char *bf, size_t bfsize, bool show_dso)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300405{
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300406 int printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300407
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300408 if (cl->ms.sym)
409 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
410 else
411 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
412
413 if (show_dso)
414 scnprintf(bf + printed, bfsize - printed, " %s",
415 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
416
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300417 return bf;
418}
419
420#define LEVEL_OFFSET_STEP 3
421
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300422static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300423 struct callchain_node *chain_node,
424 u64 total, int level,
425 unsigned short row,
426 off_t *row_offset,
427 bool *is_current_entry)
428{
429 struct rb_node *node;
430 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
431 u64 new_total, remaining;
432
433 if (callchain_param.mode == CHAIN_GRAPH_REL)
434 new_total = chain_node->children_hit;
435 else
436 new_total = total;
437
438 remaining = new_total;
439 node = rb_first(&chain_node->rb_root);
440 while (node) {
441 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
442 struct rb_node *next = rb_next(node);
Frederic Weisbeckerf08c3152011-01-14 04:51:59 +0100443 u64 cumul = callchain_cumul_hits(child);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300444 struct callchain_list *chain;
445 char folded_sign = ' ';
446 int first = true;
447 int extra_offset = 0;
448
449 remaining -= cumul;
450
451 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300452 char bf[1024], *alloc_str;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300453 const char *str;
454 int color;
455 bool was_first = first;
456
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300457 if (first)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300458 first = false;
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300459 else
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300460 extra_offset = LEVEL_OFFSET_STEP;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300461
462 folded_sign = callchain_list__folded(chain);
463 if (*row_offset != 0) {
464 --*row_offset;
465 goto do_next;
466 }
467
468 alloc_str = NULL;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300469 str = callchain_list__sym_name(chain, bf, sizeof(bf),
470 browser->show_dso);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300471 if (was_first) {
472 double percent = cumul * 100.0 / new_total;
473
474 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
475 str = "Not enough memory!";
476 else
477 str = alloc_str;
478 }
479
480 color = HE_COLORSET_NORMAL;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300481 width = browser->b.width - (offset + extra_offset + 2);
482 if (ui_browser__is_current_entry(&browser->b, row)) {
483 browser->selection = &chain->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300484 color = HE_COLORSET_SELECTED;
485 *is_current_entry = true;
486 }
487
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300488 ui_browser__set_color(&browser->b, color);
489 ui_browser__gotorc(&browser->b, row, 0);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300490 slsmg_write_nstring(" ", offset + extra_offset);
491 slsmg_printf("%c ", folded_sign);
492 slsmg_write_nstring(str, width);
493 free(alloc_str);
494
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300495 if (++row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300496 goto out;
497do_next:
498 if (folded_sign == '+')
499 break;
500 }
501
502 if (folded_sign == '-') {
503 const int new_level = level + (extra_offset ? 2 : 1);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300504 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300505 new_level, row, row_offset,
506 is_current_entry);
507 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300508 if (row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300509 goto out;
510 node = next;
511 }
512out:
513 return row - first_row;
514}
515
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300516static int hist_browser__show_callchain_node(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300517 struct callchain_node *node,
518 int level, unsigned short row,
519 off_t *row_offset,
520 bool *is_current_entry)
521{
522 struct callchain_list *chain;
523 int first_row = row,
524 offset = level * LEVEL_OFFSET_STEP,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300525 width = browser->b.width - offset;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300526 char folded_sign = ' ';
527
528 list_for_each_entry(chain, &node->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300529 char bf[1024], *s;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300530 int color;
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300531
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300532 folded_sign = callchain_list__folded(chain);
533
534 if (*row_offset != 0) {
535 --*row_offset;
536 continue;
537 }
538
539 color = HE_COLORSET_NORMAL;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300540 if (ui_browser__is_current_entry(&browser->b, row)) {
541 browser->selection = &chain->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300542 color = HE_COLORSET_SELECTED;
543 *is_current_entry = true;
544 }
545
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300546 s = callchain_list__sym_name(chain, bf, sizeof(bf),
547 browser->show_dso);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300548 ui_browser__gotorc(&browser->b, row, 0);
549 ui_browser__set_color(&browser->b, color);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300550 slsmg_write_nstring(" ", offset);
551 slsmg_printf("%c ", folded_sign);
552 slsmg_write_nstring(s, width - 2);
553
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300554 if (++row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300555 goto out;
556 }
557
558 if (folded_sign == '-')
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300559 row += hist_browser__show_callchain_node_rb_tree(browser, node,
560 browser->hists->stats.total_period,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300561 level + 1, row,
562 row_offset,
563 is_current_entry);
564out:
565 return row - first_row;
566}
567
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300568static int hist_browser__show_callchain(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300569 struct rb_root *chain,
570 int level, unsigned short row,
571 off_t *row_offset,
572 bool *is_current_entry)
573{
574 struct rb_node *nd;
575 int first_row = row;
576
577 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
578 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
579
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300580 row += hist_browser__show_callchain_node(browser, node, level,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300581 row, row_offset,
582 is_current_entry);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300583 if (row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300584 break;
585 }
586
587 return row - first_row;
588}
589
Namhyung Kim89701462013-01-22 18:09:38 +0900590struct hpp_arg {
591 struct ui_browser *b;
592 char folded_sign;
593 bool current_entry;
594};
595
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900596static int __hpp__overhead_callback(struct perf_hpp *hpp, bool front)
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900597{
Namhyung Kim89701462013-01-22 18:09:38 +0900598 struct hpp_arg *arg = hpp->ptr;
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900599
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900600 if (arg->current_entry && arg->b->navkeypressed)
601 ui_browser__set_color(arg->b, HE_COLORSET_SELECTED);
602 else
603 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
604
605 if (front) {
606 if (!symbol_conf.use_callchain)
607 return 0;
608
609 slsmg_printf("%c ", arg->folded_sign);
610 return 2;
611 }
612
613 return 0;
614}
615
616static int __hpp__color_callback(struct perf_hpp *hpp, bool front __maybe_unused)
617{
618 struct hpp_arg *arg = hpp->ptr;
619
620 if (!arg->current_entry || !arg->b->navkeypressed)
621 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
622 return 0;
623}
624
625static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
626{
627 struct hpp_arg *arg = hpp->ptr;
628 int ret;
629 va_list args;
630 double percent;
631
632 va_start(args, fmt);
633 percent = va_arg(args, double);
634 va_end(args);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900635
Namhyung Kim89701462013-01-22 18:09:38 +0900636 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900637
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900638 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
Namhyung Kim89701462013-01-22 18:09:38 +0900639 slsmg_printf("%s", hpp->buf);
640
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900641 advance_hpp(hpp, ret);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900642 return ret;
Namhyung Kimf5951d52012-09-03 11:53:09 +0900643}
644
Namhyung Kim89701462013-01-22 18:09:38 +0900645#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900646static u64 __hpp_get_##_field(struct hist_entry *he) \
647{ \
648 return he->stat._field; \
649} \
650 \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100651static int \
652hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
653 struct perf_hpp *hpp, \
654 struct hist_entry *he) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900655{ \
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900656 return __hpp__fmt(hpp, he, __hpp_get_##_field, _cb, " %6.2f%%", \
657 __hpp__slsmg_color_printf, true); \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900658}
Namhyung Kimf5951d52012-09-03 11:53:09 +0900659
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900660__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__overhead_callback)
661__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, __hpp__color_callback)
662__HPP_COLOR_PERCENT_FN(overhead_us, period_us, __hpp__color_callback)
663__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, __hpp__color_callback)
664__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, __hpp__color_callback)
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900665
666#undef __HPP_COLOR_PERCENT_FN
Namhyung Kimf5951d52012-09-03 11:53:09 +0900667
668void hist_browser__init_hpp(void)
669{
Jiri Olsa1d778222012-10-04 21:49:39 +0900670 perf_hpp__init();
Namhyung Kimf5951d52012-09-03 11:53:09 +0900671
672 perf_hpp__format[PERF_HPP__OVERHEAD].color =
673 hist_browser__hpp_color_overhead;
674 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
675 hist_browser__hpp_color_overhead_sys;
676 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
677 hist_browser__hpp_color_overhead_us;
678 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
679 hist_browser__hpp_color_overhead_guest_sys;
680 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
681 hist_browser__hpp_color_overhead_guest_us;
682}
683
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300684static int hist_browser__show_entry(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300685 struct hist_entry *entry,
686 unsigned short row)
687{
688 char s[256];
Jiri Olsa12400052012-10-13 00:06:16 +0200689 int printed = 0;
Namhyung Kim67d25912012-09-12 15:35:06 +0900690 int width = browser->b.width;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300691 char folded_sign = ' ';
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300692 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300693 off_t row_offset = entry->row_offset;
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300694 bool first = true;
Jiri Olsa12400052012-10-13 00:06:16 +0200695 struct perf_hpp_fmt *fmt;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300696
697 if (current_entry) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300698 browser->he_selection = entry;
699 browser->selection = &entry->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300700 }
701
702 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300703 hist_entry__init_have_children(entry);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300704 folded_sign = hist_entry__folded(entry);
705 }
706
707 if (row_offset == 0) {
Namhyung Kim89701462013-01-22 18:09:38 +0900708 struct hpp_arg arg = {
709 .b = &browser->b,
710 .folded_sign = folded_sign,
711 .current_entry = current_entry,
712 };
Namhyung Kimf5951d52012-09-03 11:53:09 +0900713 struct perf_hpp hpp = {
714 .buf = s,
715 .size = sizeof(s),
Namhyung Kim89701462013-01-22 18:09:38 +0900716 .ptr = &arg,
Namhyung Kimf5951d52012-09-03 11:53:09 +0900717 };
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300718
Namhyung Kim67d25912012-09-12 15:35:06 +0900719 ui_browser__gotorc(&browser->b, row, 0);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900720
Jiri Olsa12400052012-10-13 00:06:16 +0200721 perf_hpp__for_each_format(fmt) {
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300722 if (!first) {
Namhyung Kimf5951d52012-09-03 11:53:09 +0900723 slsmg_printf(" ");
724 width -= 2;
725 }
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300726 first = false;
Namhyung Kimf5951d52012-09-03 11:53:09 +0900727
Jiri Olsa12400052012-10-13 00:06:16 +0200728 if (fmt->color) {
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100729 width -= fmt->color(fmt, &hpp, entry);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900730 } else {
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100731 width -= fmt->entry(fmt, &hpp, entry);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900732 slsmg_printf("%s", s);
733 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300734 }
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -0200735
736 /* The scroll bar isn't being used */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300737 if (!browser->b.navkeypressed)
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -0200738 width += 1;
739
Namhyung Kimf5951d52012-09-03 11:53:09 +0900740 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300741 slsmg_write_nstring(s, width);
742 ++row;
743 ++printed;
744 } else
745 --row_offset;
746
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300747 if (folded_sign == '-' && row != browser->b.height) {
748 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300749 1, row, &row_offset,
750 &current_entry);
751 if (current_entry)
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300752 browser->he_selection = entry;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300753 }
754
755 return printed;
756}
757
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -0300758static void ui_browser__hists_init_top(struct ui_browser *browser)
759{
760 if (browser->top == NULL) {
761 struct hist_browser *hb;
762
763 hb = container_of(browser, struct hist_browser, b);
764 browser->top = rb_first(&hb->hists->entries);
765 }
766}
767
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300768static unsigned int hist_browser__refresh(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300769{
770 unsigned row = 0;
771 struct rb_node *nd;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300772 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300773
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300774 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300775
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300776 for (nd = browser->top; nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300777 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimf2148332014-01-14 11:52:48 +0900778 u64 total = hists__total_period(h->hists);
779 float percent = 0.0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300780
781 if (h->filtered)
782 continue;
783
Namhyung Kimf2148332014-01-14 11:52:48 +0900784 if (total)
785 percent = h->stat.period * 100.0 / total;
786
Namhyung Kim064f1982013-05-14 11:09:04 +0900787 if (percent < hb->min_pcnt)
788 continue;
789
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300790 row += hist_browser__show_entry(hb, h, row);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300791 if (row == browser->height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300792 break;
793 }
794
795 return row;
796}
797
Namhyung Kim064f1982013-05-14 11:09:04 +0900798static struct rb_node *hists__filter_entries(struct rb_node *nd,
799 struct hists *hists,
800 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300801{
802 while (nd != NULL) {
803 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimf2148332014-01-14 11:52:48 +0900804 u64 total = hists__total_period(hists);
805 float percent = 0.0;
806
807 if (total)
808 percent = h->stat.period * 100.0 / total;
Namhyung Kim064f1982013-05-14 11:09:04 +0900809
810 if (percent < min_pcnt)
811 return NULL;
812
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300813 if (!h->filtered)
814 return nd;
815
816 nd = rb_next(nd);
817 }
818
819 return NULL;
820}
821
Namhyung Kim064f1982013-05-14 11:09:04 +0900822static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
823 struct hists *hists,
824 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300825{
826 while (nd != NULL) {
827 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimf2148332014-01-14 11:52:48 +0900828 u64 total = hists__total_period(hists);
829 float percent = 0.0;
830
831 if (total)
832 percent = h->stat.period * 100.0 / total;
Namhyung Kim064f1982013-05-14 11:09:04 +0900833
834 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300835 return nd;
836
837 nd = rb_prev(nd);
838 }
839
840 return NULL;
841}
842
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300843static void ui_browser__hists_seek(struct ui_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300844 off_t offset, int whence)
845{
846 struct hist_entry *h;
847 struct rb_node *nd;
848 bool first = true;
Namhyung Kim064f1982013-05-14 11:09:04 +0900849 struct hist_browser *hb;
850
851 hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300852
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300853 if (browser->nr_entries == 0)
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -0300854 return;
855
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300856 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -0300857
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300858 switch (whence) {
859 case SEEK_SET:
Namhyung Kim064f1982013-05-14 11:09:04 +0900860 nd = hists__filter_entries(rb_first(browser->entries),
861 hb->hists, hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300862 break;
863 case SEEK_CUR:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300864 nd = browser->top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300865 goto do_offset;
866 case SEEK_END:
Namhyung Kim064f1982013-05-14 11:09:04 +0900867 nd = hists__filter_prev_entries(rb_last(browser->entries),
868 hb->hists, hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300869 first = false;
870 break;
871 default:
872 return;
873 }
874
875 /*
876 * Moves not relative to the first visible entry invalidates its
877 * row_offset:
878 */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300879 h = rb_entry(browser->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300880 h->row_offset = 0;
881
882 /*
883 * Here we have to check if nd is expanded (+), if it is we can't go
884 * the next top level hist_entry, instead we must compute an offset of
885 * what _not_ to show and not change the first visible entry.
886 *
887 * This offset increments when we are going from top to bottom and
888 * decreases when we're going from bottom to top.
889 *
890 * As we don't have backpointers to the top level in the callchains
891 * structure, we need to always print the whole hist_entry callchain,
892 * skipping the first ones that are before the first visible entry
893 * and stop when we printed enough lines to fill the screen.
894 */
895do_offset:
896 if (offset > 0) {
897 do {
898 h = rb_entry(nd, struct hist_entry, rb_node);
899 if (h->ms.unfolded) {
900 u16 remaining = h->nr_rows - h->row_offset;
901 if (offset > remaining) {
902 offset -= remaining;
903 h->row_offset = 0;
904 } else {
905 h->row_offset += offset;
906 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300907 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300908 break;
909 }
910 }
Namhyung Kim064f1982013-05-14 11:09:04 +0900911 nd = hists__filter_entries(rb_next(nd), hb->hists,
912 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300913 if (nd == NULL)
914 break;
915 --offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300916 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300917 } while (offset != 0);
918 } else if (offset < 0) {
919 while (1) {
920 h = rb_entry(nd, struct hist_entry, rb_node);
921 if (h->ms.unfolded) {
922 if (first) {
923 if (-offset > h->row_offset) {
924 offset += h->row_offset;
925 h->row_offset = 0;
926 } else {
927 h->row_offset += offset;
928 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300929 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300930 break;
931 }
932 } else {
933 if (-offset > h->nr_rows) {
934 offset += h->nr_rows;
935 h->row_offset = 0;
936 } else {
937 h->row_offset = h->nr_rows + offset;
938 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300939 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300940 break;
941 }
942 }
943 }
944
Namhyung Kim064f1982013-05-14 11:09:04 +0900945 nd = hists__filter_prev_entries(rb_prev(nd), hb->hists,
946 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300947 if (nd == NULL)
948 break;
949 ++offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300950 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300951 if (offset == 0) {
952 /*
953 * Last unfiltered hist_entry, check if it is
954 * unfolded, if it is then we should have
955 * row_offset at its last entry.
956 */
957 h = rb_entry(nd, struct hist_entry, rb_node);
958 if (h->ms.unfolded)
959 h->row_offset = h->nr_rows;
960 break;
961 }
962 first = false;
963 }
964 } else {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300965 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300966 h = rb_entry(nd, struct hist_entry, rb_node);
967 h->row_offset = 0;
968 }
969}
970
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -0300971static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
972 struct callchain_node *chain_node,
973 u64 total, int level,
974 FILE *fp)
975{
976 struct rb_node *node;
977 int offset = level * LEVEL_OFFSET_STEP;
978 u64 new_total, remaining;
979 int printed = 0;
980
981 if (callchain_param.mode == CHAIN_GRAPH_REL)
982 new_total = chain_node->children_hit;
983 else
984 new_total = total;
985
986 remaining = new_total;
987 node = rb_first(&chain_node->rb_root);
988 while (node) {
989 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
990 struct rb_node *next = rb_next(node);
991 u64 cumul = callchain_cumul_hits(child);
992 struct callchain_list *chain;
993 char folded_sign = ' ';
994 int first = true;
995 int extra_offset = 0;
996
997 remaining -= cumul;
998
999 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001000 char bf[1024], *alloc_str;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001001 const char *str;
1002 bool was_first = first;
1003
1004 if (first)
1005 first = false;
1006 else
1007 extra_offset = LEVEL_OFFSET_STEP;
1008
1009 folded_sign = callchain_list__folded(chain);
1010
1011 alloc_str = NULL;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001012 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1013 browser->show_dso);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001014 if (was_first) {
1015 double percent = cumul * 100.0 / new_total;
1016
1017 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1018 str = "Not enough memory!";
1019 else
1020 str = alloc_str;
1021 }
1022
1023 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1024 free(alloc_str);
1025 if (folded_sign == '+')
1026 break;
1027 }
1028
1029 if (folded_sign == '-') {
1030 const int new_level = level + (extra_offset ? 2 : 1);
1031 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1032 new_level, fp);
1033 }
1034
1035 node = next;
1036 }
1037
1038 return printed;
1039}
1040
1041static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1042 struct callchain_node *node,
1043 int level, FILE *fp)
1044{
1045 struct callchain_list *chain;
1046 int offset = level * LEVEL_OFFSET_STEP;
1047 char folded_sign = ' ';
1048 int printed = 0;
1049
1050 list_for_each_entry(chain, &node->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001051 char bf[1024], *s;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001052
1053 folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001054 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001055 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1056 }
1057
1058 if (folded_sign == '-')
1059 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1060 browser->hists->stats.total_period,
1061 level + 1, fp);
1062 return printed;
1063}
1064
1065static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1066 struct rb_root *chain, int level, FILE *fp)
1067{
1068 struct rb_node *nd;
1069 int printed = 0;
1070
1071 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1072 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1073
1074 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1075 }
1076
1077 return printed;
1078}
1079
1080static int hist_browser__fprintf_entry(struct hist_browser *browser,
1081 struct hist_entry *he, FILE *fp)
1082{
1083 char s[8192];
1084 double percent;
1085 int printed = 0;
1086 char folded_sign = ' ';
1087
1088 if (symbol_conf.use_callchain)
1089 folded_sign = hist_entry__folded(he);
1090
Namhyung Kim000078b2012-08-20 13:52:06 +09001091 hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
Namhyung Kimb24c28f2012-10-04 21:49:41 +09001092 percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001093
1094 if (symbol_conf.use_callchain)
1095 printed += fprintf(fp, "%c ", folded_sign);
1096
1097 printed += fprintf(fp, " %5.2f%%", percent);
1098
1099 if (symbol_conf.show_nr_samples)
Namhyung Kimb24c28f2012-10-04 21:49:41 +09001100 printed += fprintf(fp, " %11u", he->stat.nr_events);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001101
1102 if (symbol_conf.show_total_period)
Namhyung Kimb24c28f2012-10-04 21:49:41 +09001103 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001104
1105 printed += fprintf(fp, "%s\n", rtrim(s));
1106
1107 if (folded_sign == '-')
1108 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1109
1110 return printed;
1111}
1112
1113static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1114{
Namhyung Kim064f1982013-05-14 11:09:04 +09001115 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1116 browser->hists,
1117 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001118 int printed = 0;
1119
1120 while (nd) {
1121 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1122
1123 printed += hist_browser__fprintf_entry(browser, h, fp);
Namhyung Kim064f1982013-05-14 11:09:04 +09001124 nd = hists__filter_entries(rb_next(nd), browser->hists,
1125 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001126 }
1127
1128 return printed;
1129}
1130
1131static int hist_browser__dump(struct hist_browser *browser)
1132{
1133 char filename[64];
1134 FILE *fp;
1135
1136 while (1) {
1137 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1138 if (access(filename, F_OK))
1139 break;
1140 /*
1141 * XXX: Just an arbitrary lazy upper limit
1142 */
1143 if (++browser->print_seq == 8192) {
1144 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1145 return -1;
1146 }
1147 }
1148
1149 fp = fopen(filename, "w");
1150 if (fp == NULL) {
1151 char bf[64];
Kirill A. Shutemov4cc49d42012-07-24 00:06:54 +03001152 const char *err = strerror_r(errno, bf, sizeof(bf));
1153 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001154 return -1;
1155 }
1156
1157 ++browser->print_seq;
1158 hist_browser__fprintf(browser, fp);
1159 fclose(fp);
1160 ui_helpline__fpush("%s written!", filename);
1161
1162 return 0;
1163}
1164
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001165static struct hist_browser *hist_browser__new(struct hists *hists)
1166{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001167 struct hist_browser *browser = zalloc(sizeof(*browser));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001168
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001169 if (browser) {
1170 browser->hists = hists;
1171 browser->b.refresh = hist_browser__refresh;
1172 browser->b.seek = ui_browser__hists_seek;
1173 browser->b.use_navkeypressed = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001174 }
1175
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001176 return browser;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001177}
1178
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001179static void hist_browser__delete(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001180{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001181 free(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001182}
1183
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001184static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001185{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001186 return browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001187}
1188
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001189static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001190{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001191 return browser->he_selection->thread;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001192}
1193
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001194static int hists__browser_title(struct hists *hists, char *bf, size_t size,
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001195 const char *ev_name)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001196{
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001197 char unit;
1198 int printed;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001199 const struct dso *dso = hists->dso_filter;
1200 const struct thread *thread = hists->thread_filter;
1201 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1202 u64 nr_events = hists->stats.total_period;
Namhyung Kim717e2632013-01-22 18:09:44 +09001203 struct perf_evsel *evsel = hists_to_evsel(hists);
1204 char buf[512];
1205 size_t buflen = sizeof(buf);
1206
Namhyung Kimf2148332014-01-14 11:52:48 +09001207 if (symbol_conf.filter_relative) {
1208 nr_samples = hists->stats.nr_non_filtered_samples;
1209 nr_events = hists->stats.total_non_filtered_period;
1210 }
1211
Namhyung Kim759ff492013-03-05 14:53:26 +09001212 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001213 struct perf_evsel *pos;
1214
1215 perf_evsel__group_desc(evsel, buf, buflen);
1216 ev_name = buf;
1217
1218 for_each_group_member(pos, evsel) {
Namhyung Kimf2148332014-01-14 11:52:48 +09001219 if (symbol_conf.filter_relative) {
1220 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1221 nr_events += pos->hists.stats.total_non_filtered_period;
1222 } else {
1223 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1224 nr_events += pos->hists.stats.total_period;
1225 }
Namhyung Kim717e2632013-01-22 18:09:44 +09001226 }
1227 }
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001228
Ashay Ranecc686282012-04-05 21:01:01 -05001229 nr_samples = convert_unit(nr_samples, &unit);
1230 printed = scnprintf(bf, size,
1231 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1232 nr_samples, unit, ev_name, nr_events);
1233
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001234
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001235 if (hists->uid_filter_str)
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02001236 printed += snprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001237 ", UID: %s", hists->uid_filter_str);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001238 if (thread)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001239 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001240 ", Thread: %s(%d)",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001241 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001242 thread->tid);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001243 if (dso)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001244 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001245 ", DSO: %s", dso->short_name);
1246 return printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001247}
1248
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001249static inline void free_popup_options(char **options, int n)
1250{
1251 int i;
1252
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -03001253 for (i = 0; i < n; ++i)
1254 zfree(&options[i]);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001255}
1256
Feng Tangc77d8d72012-11-01 00:00:55 +08001257/* Check whether the browser is for 'top' or 'report' */
1258static inline bool is_report_browser(void *timer)
1259{
1260 return timer == NULL;
1261}
1262
Feng Tang341487ab2013-02-03 14:38:20 +08001263/*
1264 * Only runtime switching of perf data file will make "input_name" point
1265 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1266 * whether we need to call free() for current "input_name" during the switch.
1267 */
1268static bool is_input_name_malloced = false;
1269
1270static int switch_data_file(void)
1271{
1272 char *pwd, *options[32], *abs_path[32], *tmp;
1273 DIR *pwd_dir;
1274 int nr_options = 0, choice = -1, ret = -1;
1275 struct dirent *dent;
1276
1277 pwd = getenv("PWD");
1278 if (!pwd)
1279 return ret;
1280
1281 pwd_dir = opendir(pwd);
1282 if (!pwd_dir)
1283 return ret;
1284
1285 memset(options, 0, sizeof(options));
1286 memset(options, 0, sizeof(abs_path));
1287
1288 while ((dent = readdir(pwd_dir))) {
1289 char path[PATH_MAX];
1290 u64 magic;
1291 char *name = dent->d_name;
1292 FILE *file;
1293
1294 if (!(dent->d_type == DT_REG))
1295 continue;
1296
1297 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1298
1299 file = fopen(path, "r");
1300 if (!file)
1301 continue;
1302
1303 if (fread(&magic, 1, 8, file) < 8)
1304 goto close_file_and_continue;
1305
1306 if (is_perf_magic(magic)) {
1307 options[nr_options] = strdup(name);
1308 if (!options[nr_options])
1309 goto close_file_and_continue;
1310
1311 abs_path[nr_options] = strdup(path);
1312 if (!abs_path[nr_options]) {
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -03001313 zfree(&options[nr_options]);
Feng Tang341487ab2013-02-03 14:38:20 +08001314 ui__warning("Can't search all data files due to memory shortage.\n");
1315 fclose(file);
1316 break;
1317 }
1318
1319 nr_options++;
1320 }
1321
1322close_file_and_continue:
1323 fclose(file);
1324 if (nr_options >= 32) {
1325 ui__warning("Too many perf data files in PWD!\n"
1326 "Only the first 32 files will be listed.\n");
1327 break;
1328 }
1329 }
1330 closedir(pwd_dir);
1331
1332 if (nr_options) {
1333 choice = ui__popup_menu(nr_options, options);
1334 if (choice < nr_options && choice >= 0) {
1335 tmp = strdup(abs_path[choice]);
1336 if (tmp) {
1337 if (is_input_name_malloced)
1338 free((void *)input_name);
1339 input_name = tmp;
1340 is_input_name_malloced = true;
1341 ret = 0;
1342 } else
1343 ui__warning("Data switch failed due to memory shortage!\n");
1344 }
1345 }
1346
1347 free_popup_options(options, nr_options);
1348 free_popup_options(abs_path, nr_options);
1349 return ret;
1350}
1351
Namhyung Kim112f7612014-04-22 14:05:35 +09001352static void hist_browser__update_nr_entries(struct hist_browser *hb)
Namhyung Kim064f1982013-05-14 11:09:04 +09001353{
1354 u64 nr_entries = 0;
1355 struct rb_node *nd = rb_first(&hb->hists->entries);
1356
Namhyung Kim268397c2014-04-22 14:49:31 +09001357 if (hb->min_pcnt == 0) {
1358 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1359 return;
1360 }
1361
Namhyung Kimc481f932014-04-22 13:56:11 +09001362 while ((nd = hists__filter_entries(nd, hb->hists,
1363 hb->min_pcnt)) != NULL) {
Namhyung Kim064f1982013-05-14 11:09:04 +09001364 nr_entries++;
Namhyung Kimc481f932014-04-22 13:56:11 +09001365 nd = rb_next(nd);
Namhyung Kim064f1982013-05-14 11:09:04 +09001366 }
1367
Namhyung Kim112f7612014-04-22 14:05:35 +09001368 hb->nr_non_filtered_entries = nr_entries;
Namhyung Kim064f1982013-05-14 11:09:04 +09001369}
Feng Tang341487ab2013-02-03 14:38:20 +08001370
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001371static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001372 const char *helpline, const char *ev_name,
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001373 bool left_exits,
Namhyung Kim68d80752012-11-02 14:50:06 +09001374 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001375 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001376 struct perf_session_env *env)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001377{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001378 struct hists *hists = &evsel->hists;
1379 struct hist_browser *browser = hist_browser__new(hists);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001380 struct branch_info *bi;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001381 struct pstack *fstack;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001382 char *options[16];
1383 int nr_options = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001384 int key = -1;
Namhyung Kim938a23a2012-03-16 17:50:53 +09001385 char buf[64];
Feng Tangcdbab7c2012-10-30 11:56:06 +08001386 char script_opt[64];
Namhyung Kim9783adf2012-11-02 14:50:05 +09001387 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001388
Namhyung Kime8e684a2013-12-26 14:37:58 +09001389#define HIST_BROWSER_HELP_COMMON \
1390 "h/?/F1 Show this window\n" \
1391 "UP/DOWN/PGUP\n" \
1392 "PGDN/SPACE Navigate\n" \
1393 "q/ESC/CTRL+C Exit browser\n\n" \
1394 "For multiple event sessions:\n\n" \
1395 "TAB/UNTAB Switch events\n\n" \
1396 "For symbolic views (--sort has sym):\n\n" \
1397 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1398 "<- Zoom out\n" \
1399 "a Annotate current symbol\n" \
1400 "C Collapse all callchains\n" \
1401 "d Zoom into current DSO\n" \
1402 "E Expand all callchains\n" \
Namhyung Kim105eb302014-02-10 11:20:10 +09001403 "F Toggle percentage of filtered entries\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09001404
1405 /* help messages are sorted by lexical order of the hotkey */
1406 const char report_help[] = HIST_BROWSER_HELP_COMMON
Namhyung Kim6dd60132013-12-26 14:37:59 +09001407 "i Show header information\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09001408 "P Print histograms to perf.hist.N\n"
1409 "r Run available scripts\n"
1410 "s Switch to another data file in PWD\n"
1411 "t Zoom into current Thread\n"
1412 "V Verbose (DSO names in callchains, etc)\n"
1413 "/ Filter symbol by name";
1414 const char top_help[] = HIST_BROWSER_HELP_COMMON
1415 "P Print histograms to perf.hist.N\n"
1416 "t Zoom into current Thread\n"
1417 "V Verbose (DSO names in callchains, etc)\n"
1418 "/ Filter symbol by name";
1419
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001420 if (browser == NULL)
1421 return -1;
1422
Namhyung Kim064f1982013-05-14 11:09:04 +09001423 if (min_pcnt) {
1424 browser->min_pcnt = min_pcnt;
Namhyung Kim112f7612014-04-22 14:05:35 +09001425 hist_browser__update_nr_entries(browser);
Namhyung Kim064f1982013-05-14 11:09:04 +09001426 }
1427
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001428 fstack = pstack__new(2);
1429 if (fstack == NULL)
1430 goto out;
1431
1432 ui_helpline__push(helpline);
1433
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001434 memset(options, 0, sizeof(options));
1435
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001436 while (1) {
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001437 const struct thread *thread = NULL;
1438 const struct dso *dso = NULL;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001439 int choice = 0,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001440 annotate = -2, zoom_dso = -2, zoom_thread = -2,
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001441 annotate_f = -2, annotate_t = -2, browse_map = -2;
Feng Tang341487ab2013-02-03 14:38:20 +08001442 int scripts_comm = -2, scripts_symbol = -2,
1443 scripts_all = -2, switch_data = -2;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001444
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001445 nr_options = 0;
1446
Namhyung Kim9783adf2012-11-02 14:50:05 +09001447 key = hist_browser__run(browser, ev_name, hbt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001448
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001449 if (browser->he_selection != NULL) {
1450 thread = hist_browser__selected_thread(browser);
1451 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1452 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001453 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001454 case K_TAB:
1455 case K_UNTAB:
David Aherne4419b82011-10-19 11:37:47 -06001456 if (nr_events == 1)
1457 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001458 /*
1459 * Exit the browser, let hists__browser_tree
1460 * go to the next or previous
1461 */
1462 goto out_free_stack;
1463 case 'a':
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03001464 if (!sort__has_sym) {
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001465 ui_browser__warning(&browser->b, delay_secs * 2,
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02001466 "Annotation is only available for symbolic views, "
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001467 "include \"sym*\" in --sort to use it.");
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02001468 continue;
1469 }
1470
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001471 if (browser->selection == NULL ||
Lin Mingdb9a9cbc2011-04-08 14:31:26 +08001472 browser->selection->sym == NULL ||
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001473 browser->selection->map->dso->annotate_warned)
1474 continue;
1475 goto do_annotate;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001476 case 'P':
1477 hist_browser__dump(browser);
1478 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001479 case 'd':
1480 goto zoom_dso;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001481 case 'V':
1482 browser->show_dso = !browser->show_dso;
1483 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001484 case 't':
1485 goto zoom_thread;
Arnaldo Carvalho de Melo5a5626b2012-05-09 12:21:22 -03001486 case '/':
Namhyung Kim938a23a2012-03-16 17:50:53 +09001487 if (ui_browser__input_window("Symbol to show",
1488 "Please enter the name of symbol you want to see",
1489 buf, "ENTER: OK, ESC: Cancel",
1490 delay_secs * 2) == K_ENTER) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001491 hists->symbol_filter_str = *buf ? buf : NULL;
1492 hists__filter_by_symbol(hists);
Namhyung Kim938a23a2012-03-16 17:50:53 +09001493 hist_browser__reset(browser);
1494 }
1495 continue;
Feng Tangcdbab7c2012-10-30 11:56:06 +08001496 case 'r':
Namhyung Kim9783adf2012-11-02 14:50:05 +09001497 if (is_report_browser(hbt))
Feng Tangc77d8d72012-11-01 00:00:55 +08001498 goto do_scripts;
1499 continue;
Feng Tang341487ab2013-02-03 14:38:20 +08001500 case 's':
1501 if (is_report_browser(hbt))
1502 goto do_data_switch;
1503 continue;
Namhyung Kim6dd60132013-12-26 14:37:59 +09001504 case 'i':
1505 /* env->arch is NULL for live-mode (i.e. perf top) */
1506 if (env->arch)
1507 tui__header_window(env);
1508 continue;
Namhyung Kim105eb302014-02-10 11:20:10 +09001509 case 'F':
1510 symbol_conf.filter_relative ^= 1;
1511 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001512 case K_F1:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001513 case 'h':
1514 case '?':
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001515 ui_browser__help_window(&browser->b,
Namhyung Kime8e684a2013-12-26 14:37:58 +09001516 is_report_browser(hbt) ? report_help : top_help);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001517 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001518 case K_ENTER:
1519 case K_RIGHT:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001520 /* menu */
1521 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001522 case K_LEFT: {
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001523 const void *top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001524
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001525 if (pstack__empty(fstack)) {
1526 /*
1527 * Go back to the perf_evsel_menu__run or other user
1528 */
1529 if (left_exits)
1530 goto out_free_stack;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001531 continue;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001532 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001533 top = pstack__pop(fstack);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001534 if (top == &browser->hists->dso_filter)
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001535 goto zoom_out_dso;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001536 if (top == &browser->hists->thread_filter)
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001537 goto zoom_out_thread;
1538 continue;
1539 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001540 case K_ESC:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001541 if (!left_exits &&
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001542 !ui_browser__dialog_yesno(&browser->b,
1543 "Do you really want to exit?"))
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001544 continue;
1545 /* Fall thru */
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001546 case 'q':
1547 case CTRL('c'):
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001548 goto out_free_stack;
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001549 default:
1550 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001551 }
1552
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03001553 if (!sort__has_sym)
Arnaldo Carvalho de Melo724c9c92011-10-06 10:36:35 -03001554 goto add_exit_option;
1555
Namhyung Kim55369fc2013-04-01 20:35:20 +09001556 if (sort__mode == SORT_MODE__BRANCH) {
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001557 bi = browser->he_selection->branch_info;
1558 if (browser->selection != NULL &&
1559 bi &&
1560 bi->from.sym != NULL &&
1561 !bi->from.map->dso->annotate_warned &&
1562 asprintf(&options[nr_options], "Annotate %s",
1563 bi->from.sym->name) > 0)
1564 annotate_f = nr_options++;
1565
1566 if (browser->selection != NULL &&
1567 bi &&
1568 bi->to.sym != NULL &&
1569 !bi->to.map->dso->annotate_warned &&
Stephane Eranian8bcd65f2012-03-12 16:13:29 +01001570 (bi->to.sym != bi->from.sym ||
1571 bi->to.map->dso != bi->from.map->dso) &&
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001572 asprintf(&options[nr_options], "Annotate %s",
1573 bi->to.sym->name) > 0)
1574 annotate_t = nr_options++;
1575 } else {
1576
1577 if (browser->selection != NULL &&
1578 browser->selection->sym != NULL &&
1579 !browser->selection->map->dso->annotate_warned &&
1580 asprintf(&options[nr_options], "Annotate %s",
1581 browser->selection->sym->name) > 0)
1582 annotate = nr_options++;
1583 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001584
1585 if (thread != NULL &&
1586 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001587 (browser->hists->thread_filter ? "out of" : "into"),
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001588 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001589 thread->tid) > 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001590 zoom_thread = nr_options++;
1591
1592 if (dso != NULL &&
1593 asprintf(&options[nr_options], "Zoom %s %s DSO",
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001594 (browser->hists->dso_filter ? "out of" : "into"),
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001595 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1596 zoom_dso = nr_options++;
1597
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001598 if (browser->selection != NULL &&
1599 browser->selection->map != NULL &&
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001600 asprintf(&options[nr_options], "Browse map details") > 0)
1601 browse_map = nr_options++;
Feng Tangcdbab7c2012-10-30 11:56:06 +08001602
1603 /* perf script support */
1604 if (browser->he_selection) {
1605 struct symbol *sym;
1606
1607 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001608 thread__comm_str(browser->he_selection->thread)) > 0)
Feng Tangcdbab7c2012-10-30 11:56:06 +08001609 scripts_comm = nr_options++;
1610
1611 sym = browser->he_selection->ms.sym;
1612 if (sym && sym->namelen &&
1613 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1614 sym->name) > 0)
1615 scripts_symbol = nr_options++;
1616 }
1617
1618 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1619 scripts_all = nr_options++;
1620
Feng Tang341487ab2013-02-03 14:38:20 +08001621 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1622 "Switch to another data file in PWD") > 0)
1623 switch_data = nr_options++;
Arnaldo Carvalho de Melo724c9c92011-10-06 10:36:35 -03001624add_exit_option:
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001625 options[nr_options++] = (char *)"Exit";
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001626retry_popup_menu:
Arnaldo Carvalho de Melo1e6dd072010-08-10 15:58:50 -03001627 choice = ui__popup_menu(nr_options, options);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001628
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001629 if (choice == nr_options - 1)
1630 break;
1631
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001632 if (choice == -1) {
1633 free_popup_options(options, nr_options - 1);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001634 continue;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001635 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001636
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001637 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001638 struct hist_entry *he;
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001639 int err;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001640do_annotate:
Namhyung Kim68d80752012-11-02 14:50:06 +09001641 if (!objdump_path && perf_session_env__lookup_objdump(env))
1642 continue;
1643
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001644 he = hist_browser__selected_entry(browser);
1645 if (he == NULL)
1646 continue;
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001647
1648 /*
1649 * we stash the branch_info symbol + map into the
1650 * the ms so we don't have to rewrite all the annotation
1651 * code to use branch_info.
1652 * in branch mode, the ms struct is not used
1653 */
1654 if (choice == annotate_f) {
1655 he->ms.sym = he->branch_info->from.sym;
1656 he->ms.map = he->branch_info->from.map;
1657 } else if (choice == annotate_t) {
1658 he->ms.sym = he->branch_info->to.sym;
1659 he->ms.map = he->branch_info->to.map;
1660 }
1661
Arnaldo Carvalho de Melodf71d952011-10-13 08:01:33 -03001662 /*
1663 * Don't let this be freed, say, by hists__decay_entry.
1664 */
1665 he->used = true;
Namhyung Kimdb8fd072013-03-05 14:53:21 +09001666 err = hist_entry__tui_annotate(he, evsel, hbt);
Arnaldo Carvalho de Melodf71d952011-10-13 08:01:33 -03001667 he->used = false;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001668 /*
1669 * offer option to annotate the other branch source or target
1670 * (if they exists) when returning from annotate
1671 */
1672 if ((err == 'q' || err == CTRL('c'))
1673 && annotate_t != -2 && annotate_f != -2)
1674 goto retry_popup_menu;
1675
Arnaldo Carvalho de Melo900e14a2011-10-11 16:15:39 -03001676 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001677 if (err)
1678 ui_browser__handle_resize(&browser->b);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001679
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001680 } else if (choice == browse_map)
1681 map__browse(browser->selection->map);
1682 else if (choice == zoom_dso) {
1683zoom_dso:
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001684 if (browser->hists->dso_filter) {
1685 pstack__remove(fstack, &browser->hists->dso_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001686zoom_out_dso:
1687 ui_helpline__pop();
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001688 browser->hists->dso_filter = NULL;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001689 sort_dso.elide = false;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001690 } else {
1691 if (dso == NULL)
1692 continue;
1693 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1694 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001695 browser->hists->dso_filter = dso;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001696 sort_dso.elide = true;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001697 pstack__push(fstack, &browser->hists->dso_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001698 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001699 hists__filter_by_dso(hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001700 hist_browser__reset(browser);
1701 } else if (choice == zoom_thread) {
1702zoom_thread:
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001703 if (browser->hists->thread_filter) {
1704 pstack__remove(fstack, &browser->hists->thread_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001705zoom_out_thread:
1706 ui_helpline__pop();
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001707 browser->hists->thread_filter = NULL;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001708 sort_thread.elide = false;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001709 } else {
1710 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001711 thread->comm_set ? thread__comm_str(thread) : "",
Adrian Hunter38051232013-07-04 16:20:31 +03001712 thread->tid);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001713 browser->hists->thread_filter = thread;
Arnaldo Carvalho de Melocc02c922011-10-20 08:02:30 -02001714 sort_thread.elide = true;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001715 pstack__push(fstack, &browser->hists->thread_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001716 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001717 hists__filter_by_thread(hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001718 hist_browser__reset(browser);
1719 }
Feng Tangcdbab7c2012-10-30 11:56:06 +08001720 /* perf scripts support */
1721 else if (choice == scripts_all || choice == scripts_comm ||
1722 choice == scripts_symbol) {
1723do_scripts:
1724 memset(script_opt, 0, 64);
1725
1726 if (choice == scripts_comm)
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001727 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
Feng Tangcdbab7c2012-10-30 11:56:06 +08001728
1729 if (choice == scripts_symbol)
1730 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1731
1732 script_browse(script_opt);
1733 }
Feng Tang341487ab2013-02-03 14:38:20 +08001734 /* Switch to another data file */
1735 else if (choice == switch_data) {
1736do_data_switch:
1737 if (!switch_data_file()) {
1738 key = K_SWITCH_INPUT_DATA;
1739 break;
1740 } else
1741 ui__warning("Won't switch the data files due to\n"
1742 "no valid data file get selected!\n");
1743 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001744 }
1745out_free_stack:
1746 pstack__delete(fstack);
1747out:
1748 hist_browser__delete(browser);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001749 free_popup_options(options, nr_options - 1);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001750 return key;
1751}
1752
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001753struct perf_evsel_menu {
1754 struct ui_browser b;
1755 struct perf_evsel *selection;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001756 bool lost_events, lost_events_warned;
Namhyung Kim064f1982013-05-14 11:09:04 +09001757 float min_pcnt;
Namhyung Kim68d80752012-11-02 14:50:06 +09001758 struct perf_session_env *env;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001759};
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001760
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001761static void perf_evsel_menu__write(struct ui_browser *browser,
1762 void *entry, int row)
1763{
1764 struct perf_evsel_menu *menu = container_of(browser,
1765 struct perf_evsel_menu, b);
1766 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1767 bool current_entry = ui_browser__is_current_entry(browser, row);
1768 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001769 const char *ev_name = perf_evsel__name(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001770 char bf[256], unit;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001771 const char *warn = " ";
1772 size_t printed;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001773
1774 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1775 HE_COLORSET_NORMAL);
1776
Namhyung Kim759ff492013-03-05 14:53:26 +09001777 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001778 struct perf_evsel *pos;
1779
1780 ev_name = perf_evsel__group_name(evsel);
1781
1782 for_each_group_member(pos, evsel) {
1783 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1784 }
1785 }
1786
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001787 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001788 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001789 unit, unit == ' ' ? "" : " ", ev_name);
1790 slsmg_printf("%s", bf);
1791
1792 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1793 if (nr_events != 0) {
1794 menu->lost_events = true;
1795 if (!current_entry)
1796 ui_browser__set_color(browser, HE_COLORSET_TOP);
1797 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001798 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1799 nr_events, unit, unit == ' ' ? "" : " ");
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001800 warn = bf;
1801 }
1802
1803 slsmg_write_nstring(warn, browser->width - printed);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001804
1805 if (current_entry)
1806 menu->selection = evsel;
1807}
1808
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001809static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1810 int nr_events, const char *help,
Namhyung Kim9783adf2012-11-02 14:50:05 +09001811 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001812{
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001813 struct perf_evlist *evlist = menu->b.priv;
1814 struct perf_evsel *pos;
1815 const char *ev_name, *title = "Available samples";
Namhyung Kim9783adf2012-11-02 14:50:05 +09001816 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001817 int key;
1818
1819 if (ui_browser__show(&menu->b, title,
1820 "ESC: exit, ENTER|->: Browse histograms") < 0)
1821 return -1;
1822
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001823 while (1) {
Arnaldo Carvalho de Melo3af6e332011-10-13 08:52:46 -03001824 key = ui_browser__run(&menu->b, delay_secs);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001825
1826 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001827 case K_TIMER:
Namhyung Kim9783adf2012-11-02 14:50:05 +09001828 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001829
1830 if (!menu->lost_events_warned && menu->lost_events) {
1831 ui_browser__warn_lost_events(&menu->b);
1832 menu->lost_events_warned = true;
1833 }
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001834 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001835 case K_RIGHT:
1836 case K_ENTER:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001837 if (!menu->selection)
1838 continue;
1839 pos = menu->selection;
1840browse_hists:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001841 perf_evlist__set_selected(evlist, pos);
1842 /*
1843 * Give the calling tool a chance to populate the non
1844 * default evsel resorted hists tree.
1845 */
Namhyung Kim9783adf2012-11-02 14:50:05 +09001846 if (hbt)
1847 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001848 ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001849 key = perf_evsel__hists_browse(pos, nr_events, help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001850 ev_name, true, hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001851 menu->min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001852 menu->env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001853 ui_browser__show_title(&menu->b, title);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001854 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001855 case K_TAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001856 if (pos->node.next == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001857 pos = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001858 else
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001859 pos = perf_evsel__next(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001860 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001861 case K_UNTAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001862 if (pos->node.prev == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001863 pos = perf_evlist__last(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001864 else
Arnaldo Carvalho de Melod87fcb42013-11-13 15:56:40 -03001865 pos = perf_evsel__prev(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001866 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001867 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001868 if (!ui_browser__dialog_yesno(&menu->b,
1869 "Do you really want to exit?"))
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001870 continue;
1871 /* Fall thru */
Feng Tang341487ab2013-02-03 14:38:20 +08001872 case K_SWITCH_INPUT_DATA:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001873 case 'q':
1874 case CTRL('c'):
1875 goto out;
1876 default:
1877 continue;
1878 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001879 case K_LEFT:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001880 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001881 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001882 if (!ui_browser__dialog_yesno(&menu->b,
1883 "Do you really want to exit?"))
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001884 continue;
1885 /* Fall thru */
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001886 case 'q':
1887 case CTRL('c'):
1888 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001889 default:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001890 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001891 }
1892 }
1893
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001894out:
1895 ui_browser__hide(&menu->b);
1896 return key;
1897}
1898
Arnaldo Carvalho de Melo316c7132013-11-05 15:32:36 -03001899static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001900 void *entry)
1901{
1902 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1903
1904 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1905 return true;
1906
1907 return false;
1908}
1909
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001910static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001911 int nr_entries, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001912 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001913 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001914 struct perf_session_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001915{
1916 struct perf_evsel *pos;
1917 struct perf_evsel_menu menu = {
1918 .b = {
1919 .entries = &evlist->entries,
1920 .refresh = ui_browser__list_head_refresh,
1921 .seek = ui_browser__list_head_seek,
1922 .write = perf_evsel_menu__write,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001923 .filter = filter_group_entries,
1924 .nr_entries = nr_entries,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001925 .priv = evlist,
1926 },
Namhyung Kim064f1982013-05-14 11:09:04 +09001927 .min_pcnt = min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001928 .env = env,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001929 };
1930
1931 ui_helpline__push("Press ESC to exit");
1932
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001933 evlist__for_each(evlist, pos) {
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001934 const char *ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001935 size_t line_len = strlen(ev_name) + 7;
1936
1937 if (menu.b.width < line_len)
1938 menu.b.width = line_len;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001939 }
1940
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001941 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001942}
1943
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001944int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001945 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001946 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001947 struct perf_session_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001948{
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001949 int nr_entries = evlist->nr_entries;
1950
1951single_entry:
1952 if (nr_entries == 1) {
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001953 struct perf_evsel *first = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001954 const char *ev_name = perf_evsel__name(first);
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001955
1956 return perf_evsel__hists_browse(first, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09001957 ev_name, false, hbt, min_pcnt,
1958 env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001959 }
1960
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001961 if (symbol_conf.event_group) {
1962 struct perf_evsel *pos;
1963
1964 nr_entries = 0;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001965 evlist__for_each(evlist, pos) {
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001966 if (perf_evsel__is_group_leader(pos))
1967 nr_entries++;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001968 }
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001969
1970 if (nr_entries == 1)
1971 goto single_entry;
1972 }
1973
1974 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09001975 hbt, min_pcnt, env);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001976}