blob: 2185091c5227abfc5834003f7878c074e4c42342 [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"
Jiri Olsad7553302014-06-15 10:22:15 +020020#include "annotate.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030021
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030022struct hist_browser {
23 struct ui_browser b;
24 struct hists *hists;
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -030027 int print_seq;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -030028 bool show_dso;
Namhyung Kim064f1982013-05-14 11:09:04 +090029 float min_pcnt;
Namhyung Kim112f7612014-04-22 14:05:35 +090030 u64 nr_non_filtered_entries;
Namhyung Kimc3b78952014-04-22 15:56:17 +090031 u64 nr_callchain_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030032};
33
Namhyung Kimf5951d52012-09-03 11:53:09 +090034extern void hist_browser__init_hpp(void);
35
Jiri Olsadd00d482014-06-19 13:41:13 +020036static int hists__browser_title(struct hists *hists, char *bf, size_t size);
Namhyung Kim112f7612014-04-22 14:05:35 +090037static void hist_browser__update_nr_entries(struct hist_browser *hb);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -030038
Namhyung Kimc3b78952014-04-22 15:56:17 +090039static struct rb_node *hists__filter_entries(struct rb_node *nd,
Namhyung Kimc3b78952014-04-22 15:56:17 +090040 float min_pcnt);
41
Namhyung Kim268397c2014-04-22 14:49:31 +090042static bool hist_browser__has_filter(struct hist_browser *hb)
43{
44 return hists__has_filter(hb->hists) || hb->min_pcnt;
45}
46
Namhyung Kimc3b78952014-04-22 15:56:17 +090047static u32 hist_browser__nr_entries(struct hist_browser *hb)
48{
49 u32 nr_entries;
50
51 if (hist_browser__has_filter(hb))
52 nr_entries = hb->nr_non_filtered_entries;
53 else
54 nr_entries = hb->hists->nr_entries;
55
56 return nr_entries + hb->nr_callchain_rows;
57}
58
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030059static void hist_browser__refresh_dimensions(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030060{
61 /* 3 == +/- toggle symbol before actual hist_entry rendering */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030062 browser->b.width = 3 + (hists__sort_list_width(browser->hists) +
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030063 sizeof("[k]"));
64}
65
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030066static void hist_browser__reset(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030067{
Namhyung Kimc3b78952014-04-22 15:56:17 +090068 /*
69 * The hists__remove_entry_filter() already folds non-filtered
70 * entries so we can assume it has 0 callchain rows.
71 */
72 browser->nr_callchain_rows = 0;
73
Namhyung Kim268397c2014-04-22 14:49:31 +090074 hist_browser__update_nr_entries(browser);
Namhyung Kimc3b78952014-04-22 15:56:17 +090075 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030076 hist_browser__refresh_dimensions(browser);
77 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030078}
79
80static char tree__folded_sign(bool unfolded)
81{
82 return unfolded ? '-' : '+';
83}
84
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030085static char map_symbol__folded(const struct map_symbol *ms)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030086{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030087 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030088}
89
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030090static char hist_entry__folded(const struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030091{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030092 return map_symbol__folded(&he->ms);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030093}
94
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030095static char callchain_list__folded(const struct callchain_list *cl)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030096{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -030097 return map_symbol__folded(&cl->ms);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030098}
99
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300100static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300101{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300102 ms->unfolded = unfold ? ms->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300103}
104
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300105static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300106{
107 int n = 0;
108 struct rb_node *nd;
109
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300110 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300111 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
112 struct callchain_list *chain;
113 char folded_sign = ' '; /* No children */
114
115 list_for_each_entry(chain, &child->val, list) {
116 ++n;
117 /* We need this because we may not have children */
118 folded_sign = callchain_list__folded(chain);
119 if (folded_sign == '+')
120 break;
121 }
122
123 if (folded_sign == '-') /* Have children and they're unfolded */
124 n += callchain_node__count_rows_rb_tree(child);
125 }
126
127 return n;
128}
129
130static int callchain_node__count_rows(struct callchain_node *node)
131{
132 struct callchain_list *chain;
133 bool unfolded = false;
134 int n = 0;
135
136 list_for_each_entry(chain, &node->val, list) {
137 ++n;
138 unfolded = chain->ms.unfolded;
139 }
140
141 if (unfolded)
142 n += callchain_node__count_rows_rb_tree(node);
143
144 return n;
145}
146
147static int callchain__count_rows(struct rb_root *chain)
148{
149 struct rb_node *nd;
150 int n = 0;
151
152 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
153 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
154 n += callchain_node__count_rows(node);
155 }
156
157 return n;
158}
159
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300160static bool map_symbol__toggle_fold(struct map_symbol *ms)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300161{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300162 if (!ms)
Jiri Olsa8493fe12012-04-04 22:21:31 +0200163 return false;
164
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300165 if (!ms->has_children)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300166 return false;
167
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300168 ms->unfolded = !ms->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300169 return true;
170}
171
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300172static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300173{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300174 struct rb_node *nd = rb_first(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300175
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300176 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300177 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
178 struct callchain_list *chain;
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300179 bool first = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300180
181 list_for_each_entry(chain, &child->val, list) {
182 if (first) {
183 first = false;
184 chain->ms.has_children = chain->list.next != &child->val ||
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300185 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300186 } else
187 chain->ms.has_children = chain->list.next == &child->val &&
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300188 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300189 }
190
191 callchain_node__init_have_children_rb_tree(child);
192 }
193}
194
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300195static void callchain_node__init_have_children(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300196{
197 struct callchain_list *chain;
198
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300199 list_for_each_entry(chain, &node->val, list)
200 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300201
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300202 callchain_node__init_have_children_rb_tree(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300203}
204
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300205static void callchain__init_have_children(struct rb_root *root)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300206{
207 struct rb_node *nd;
208
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300209 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300210 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
211 callchain_node__init_have_children(node);
212 }
213}
214
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300215static void hist_entry__init_have_children(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300216{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300217 if (!he->init_have_children) {
218 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
219 callchain__init_have_children(&he->sorted_chain);
220 he->init_have_children = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300221 }
222}
223
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300224static bool hist_browser__toggle_fold(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300225{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300226 if (map_symbol__toggle_fold(browser->selection)) {
227 struct hist_entry *he = browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300228
229 hist_entry__init_have_children(he);
Namhyung Kimc3b78952014-04-22 15:56:17 +0900230 browser->b.nr_entries -= he->nr_rows;
231 browser->nr_callchain_rows -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300232
233 if (he->ms.unfolded)
234 he->nr_rows = callchain__count_rows(&he->sorted_chain);
235 else
236 he->nr_rows = 0;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900237
238 browser->b.nr_entries += he->nr_rows;
239 browser->nr_callchain_rows += he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300240
241 return true;
242 }
243
244 /* If it doesn't have children, no toggling performed */
245 return false;
246}
247
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300248static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300249{
250 int n = 0;
251 struct rb_node *nd;
252
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300253 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300254 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
255 struct callchain_list *chain;
256 bool has_children = false;
257
258 list_for_each_entry(chain, &child->val, list) {
259 ++n;
260 map_symbol__set_folding(&chain->ms, unfold);
261 has_children = chain->ms.has_children;
262 }
263
264 if (has_children)
265 n += callchain_node__set_folding_rb_tree(child, unfold);
266 }
267
268 return n;
269}
270
271static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
272{
273 struct callchain_list *chain;
274 bool has_children = false;
275 int n = 0;
276
277 list_for_each_entry(chain, &node->val, list) {
278 ++n;
279 map_symbol__set_folding(&chain->ms, unfold);
280 has_children = chain->ms.has_children;
281 }
282
283 if (has_children)
284 n += callchain_node__set_folding_rb_tree(node, unfold);
285
286 return n;
287}
288
289static int callchain__set_folding(struct rb_root *chain, bool unfold)
290{
291 struct rb_node *nd;
292 int n = 0;
293
294 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
295 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
296 n += callchain_node__set_folding(node, unfold);
297 }
298
299 return n;
300}
301
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300302static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300303{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300304 hist_entry__init_have_children(he);
305 map_symbol__set_folding(&he->ms, unfold);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300306
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300307 if (he->ms.has_children) {
308 int n = callchain__set_folding(&he->sorted_chain, unfold);
309 he->nr_rows = unfold ? n : 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300310 } else
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300311 he->nr_rows = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300312}
313
Namhyung Kimc3b78952014-04-22 15:56:17 +0900314static void
315__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300316{
317 struct rb_node *nd;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900318 struct hists *hists = browser->hists;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300319
Namhyung Kimc3b78952014-04-22 15:56:17 +0900320 for (nd = rb_first(&hists->entries);
Namhyung Kim14135662013-10-31 10:17:39 +0900321 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900322 nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300323 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
324 hist_entry__set_folding(he, unfold);
Namhyung Kimc3b78952014-04-22 15:56:17 +0900325 browser->nr_callchain_rows += he->nr_rows;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300326 }
327}
328
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300329static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300330{
Namhyung Kimc3b78952014-04-22 15:56:17 +0900331 browser->nr_callchain_rows = 0;
332 __hist_browser__set_folding(browser, unfold);
333
334 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300335 /* Go to the start, we may be way after valid entries after a collapse */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300336 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300337}
338
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200339static void ui_browser__warn_lost_events(struct ui_browser *browser)
340{
341 ui_browser__warning(browser, 4,
342 "Events are being lost, check IO/CPU overload!\n\n"
343 "You may want to run 'perf' using a RT scheduler policy:\n\n"
344 " perf top -r 80\n\n"
345 "Or reduce the sampling frequency.");
346}
347
Jiri Olsadd00d482014-06-19 13:41:13 +0200348static int hist_browser__run(struct hist_browser *browser,
Namhyung Kim9783adf2012-11-02 14:50:05 +0900349 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300350{
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300351 int key;
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300352 char title[160];
Namhyung Kim9783adf2012-11-02 14:50:05 +0900353 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300354
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300355 browser->b.entries = &browser->hists->entries;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900356 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300357
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300358 hist_browser__refresh_dimensions(browser);
Jiri Olsadd00d482014-06-19 13:41:13 +0200359 hists__browser_title(browser->hists, title, sizeof(title));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300360
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300361 if (ui_browser__show(&browser->b, title,
Arnaldo Carvalho de Melo59e8fe32010-08-10 15:44:20 -0300362 "Press '?' for help on key bindings") < 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300363 return -1;
364
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300365 while (1) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300366 key = ui_browser__run(&browser->b, delay_secs);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300367
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300368 switch (key) {
Namhyung Kimfa5df942013-05-14 11:09:05 +0900369 case K_TIMER: {
370 u64 nr_entries;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900371 hbt->timer(hbt->arg);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900372
Namhyung Kimc3b78952014-04-22 15:56:17 +0900373 if (hist_browser__has_filter(browser))
Namhyung Kim112f7612014-04-22 14:05:35 +0900374 hist_browser__update_nr_entries(browser);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900375
Namhyung Kimc3b78952014-04-22 15:56:17 +0900376 nr_entries = hist_browser__nr_entries(browser);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900377 ui_browser__update_nr_entries(&browser->b, nr_entries);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200378
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300379 if (browser->hists->stats.nr_lost_warned !=
380 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
381 browser->hists->stats.nr_lost_warned =
382 browser->hists->stats.nr_events[PERF_RECORD_LOST];
383 ui_browser__warn_lost_events(&browser->b);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200384 }
385
Jiri Olsadd00d482014-06-19 13:41:13 +0200386 hists__browser_title(browser->hists, title, sizeof(title));
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300387 ui_browser__show_title(&browser->b, title);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300388 continue;
Namhyung Kimfa5df942013-05-14 11:09:05 +0900389 }
Arnaldo Carvalho de Melo46941532010-08-10 15:50:07 -0300390 case 'D': { /* Debug */
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300391 static int seq;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300392 struct hist_entry *h = rb_entry(browser->b.top,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300393 struct hist_entry, rb_node);
394 ui_helpline__pop();
395 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 -0300396 seq++, browser->b.nr_entries,
397 browser->hists->nr_entries,
398 browser->b.height,
399 browser->b.index,
400 browser->b.top_idx,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300401 h->row_offset, h->nr_rows);
402 }
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300403 break;
404 case 'C':
405 /* Collapse the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300406 hist_browser__set_folding(browser, false);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300407 break;
408 case 'E':
409 /* Expand the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300410 hist_browser__set_folding(browser, true);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300411 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -0200412 case K_ENTER:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300413 if (hist_browser__toggle_fold(browser))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300414 break;
415 /* fall thru */
416 default:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300417 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300418 }
419 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300420out:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300421 ui_browser__hide(&browser->b);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300422 return key;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300423}
424
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300425static char *callchain_list__sym_name(struct callchain_list *cl,
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300426 char *bf, size_t bfsize, bool show_dso)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300427{
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300428 int printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300429
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300430 if (cl->ms.sym)
431 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
432 else
433 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
434
435 if (show_dso)
436 scnprintf(bf + printed, bfsize - printed, " %s",
437 cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
438
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300439 return bf;
440}
441
442#define LEVEL_OFFSET_STEP 3
443
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300444static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300445 struct callchain_node *chain_node,
446 u64 total, int level,
447 unsigned short row,
448 off_t *row_offset,
449 bool *is_current_entry)
450{
451 struct rb_node *node;
452 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
453 u64 new_total, remaining;
454
455 if (callchain_param.mode == CHAIN_GRAPH_REL)
456 new_total = chain_node->children_hit;
457 else
458 new_total = total;
459
460 remaining = new_total;
461 node = rb_first(&chain_node->rb_root);
462 while (node) {
463 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
464 struct rb_node *next = rb_next(node);
Frederic Weisbeckerf08c3152011-01-14 04:51:59 +0100465 u64 cumul = callchain_cumul_hits(child);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300466 struct callchain_list *chain;
467 char folded_sign = ' ';
468 int first = true;
469 int extra_offset = 0;
470
471 remaining -= cumul;
472
473 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300474 char bf[1024], *alloc_str;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300475 const char *str;
476 int color;
477 bool was_first = first;
478
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300479 if (first)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300480 first = false;
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300481 else
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300482 extra_offset = LEVEL_OFFSET_STEP;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300483
484 folded_sign = callchain_list__folded(chain);
485 if (*row_offset != 0) {
486 --*row_offset;
487 goto do_next;
488 }
489
490 alloc_str = NULL;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300491 str = callchain_list__sym_name(chain, bf, sizeof(bf),
492 browser->show_dso);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300493 if (was_first) {
494 double percent = cumul * 100.0 / new_total;
495
496 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
497 str = "Not enough memory!";
498 else
499 str = alloc_str;
500 }
501
502 color = HE_COLORSET_NORMAL;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300503 width = browser->b.width - (offset + extra_offset + 2);
504 if (ui_browser__is_current_entry(&browser->b, row)) {
505 browser->selection = &chain->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300506 color = HE_COLORSET_SELECTED;
507 *is_current_entry = true;
508 }
509
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300510 ui_browser__set_color(&browser->b, color);
511 ui_browser__gotorc(&browser->b, row, 0);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300512 slsmg_write_nstring(" ", offset + extra_offset);
513 slsmg_printf("%c ", folded_sign);
514 slsmg_write_nstring(str, width);
515 free(alloc_str);
516
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300517 if (++row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300518 goto out;
519do_next:
520 if (folded_sign == '+')
521 break;
522 }
523
524 if (folded_sign == '-') {
525 const int new_level = level + (extra_offset ? 2 : 1);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300526 row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300527 new_level, row, row_offset,
528 is_current_entry);
529 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300530 if (row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300531 goto out;
532 node = next;
533 }
534out:
535 return row - first_row;
536}
537
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300538static int hist_browser__show_callchain_node(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300539 struct callchain_node *node,
540 int level, unsigned short row,
541 off_t *row_offset,
542 bool *is_current_entry)
543{
544 struct callchain_list *chain;
545 int first_row = row,
546 offset = level * LEVEL_OFFSET_STEP,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300547 width = browser->b.width - offset;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300548 char folded_sign = ' ';
549
550 list_for_each_entry(chain, &node->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300551 char bf[1024], *s;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300552 int color;
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300553
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300554 folded_sign = callchain_list__folded(chain);
555
556 if (*row_offset != 0) {
557 --*row_offset;
558 continue;
559 }
560
561 color = HE_COLORSET_NORMAL;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300562 if (ui_browser__is_current_entry(&browser->b, row)) {
563 browser->selection = &chain->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300564 color = HE_COLORSET_SELECTED;
565 *is_current_entry = true;
566 }
567
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -0300568 s = callchain_list__sym_name(chain, bf, sizeof(bf),
569 browser->show_dso);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300570 ui_browser__gotorc(&browser->b, row, 0);
571 ui_browser__set_color(&browser->b, color);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300572 slsmg_write_nstring(" ", offset);
573 slsmg_printf("%c ", folded_sign);
574 slsmg_write_nstring(s, width - 2);
575
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300576 if (++row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300577 goto out;
578 }
579
580 if (folded_sign == '-')
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300581 row += hist_browser__show_callchain_node_rb_tree(browser, node,
582 browser->hists->stats.total_period,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300583 level + 1, row,
584 row_offset,
585 is_current_entry);
586out:
587 return row - first_row;
588}
589
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300590static int hist_browser__show_callchain(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300591 struct rb_root *chain,
592 int level, unsigned short row,
593 off_t *row_offset,
594 bool *is_current_entry)
595{
596 struct rb_node *nd;
597 int first_row = row;
598
599 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
600 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
601
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300602 row += hist_browser__show_callchain_node(browser, node, level,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300603 row, row_offset,
604 is_current_entry);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300605 if (row == browser->b.height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300606 break;
607 }
608
609 return row - first_row;
610}
611
Namhyung Kim89701462013-01-22 18:09:38 +0900612struct hpp_arg {
613 struct ui_browser *b;
614 char folded_sign;
615 bool current_entry;
616};
617
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900618static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
619{
620 struct hpp_arg *arg = hpp->ptr;
621 int ret;
622 va_list args;
623 double percent;
624
625 va_start(args, fmt);
626 percent = va_arg(args, double);
627 va_end(args);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900628
Namhyung Kim89701462013-01-22 18:09:38 +0900629 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900630
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900631 ret = scnprintf(hpp->buf, hpp->size, fmt, percent);
Namhyung Kim89701462013-01-22 18:09:38 +0900632 slsmg_printf("%s", hpp->buf);
633
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900634 advance_hpp(hpp, ret);
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900635 return ret;
Namhyung Kimf5951d52012-09-03 11:53:09 +0900636}
637
Namhyung Kimfb821c9e2014-03-03 17:05:19 +0900638#define __HPP_COLOR_PERCENT_FN(_type, _field) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900639static u64 __hpp_get_##_field(struct hist_entry *he) \
640{ \
641 return he->stat._field; \
642} \
643 \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100644static int \
645hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
646 struct perf_hpp *hpp, \
647 struct hist_entry *he) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900648{ \
Namhyung Kimfb821c9e2014-03-03 17:05:19 +0900649 return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \
Namhyung Kim2f6d9002014-03-03 10:14:05 +0900650 __hpp__slsmg_color_printf, true); \
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900651}
Namhyung Kimf5951d52012-09-03 11:53:09 +0900652
Namhyung Kim0434ddd2013-10-30 16:12:59 +0900653#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
654static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
655{ \
656 return he->stat_acc->_field; \
657} \
658 \
659static int \
660hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
661 struct perf_hpp *hpp, \
662 struct hist_entry *he) \
663{ \
664 if (!symbol_conf.cumulate_callchain) { \
665 int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \
666 slsmg_printf("%s", hpp->buf); \
667 \
668 return ret; \
669 } \
670 return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \
671 __hpp__slsmg_color_printf, true); \
672}
673
Namhyung Kimfb821c9e2014-03-03 17:05:19 +0900674__HPP_COLOR_PERCENT_FN(overhead, period)
675__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
676__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
677__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
678__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
Namhyung Kim0434ddd2013-10-30 16:12:59 +0900679__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
Namhyung Kim5aed9d22013-01-22 18:09:35 +0900680
681#undef __HPP_COLOR_PERCENT_FN
Namhyung Kim0434ddd2013-10-30 16:12:59 +0900682#undef __HPP_COLOR_ACC_PERCENT_FN
Namhyung Kimf5951d52012-09-03 11:53:09 +0900683
684void hist_browser__init_hpp(void)
685{
Namhyung Kimf5951d52012-09-03 11:53:09 +0900686 perf_hpp__format[PERF_HPP__OVERHEAD].color =
687 hist_browser__hpp_color_overhead;
688 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
689 hist_browser__hpp_color_overhead_sys;
690 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
691 hist_browser__hpp_color_overhead_us;
692 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
693 hist_browser__hpp_color_overhead_guest_sys;
694 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
695 hist_browser__hpp_color_overhead_guest_us;
Namhyung Kim0434ddd2013-10-30 16:12:59 +0900696 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
697 hist_browser__hpp_color_overhead_acc;
Namhyung Kimf5951d52012-09-03 11:53:09 +0900698}
699
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300700static int hist_browser__show_entry(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300701 struct hist_entry *entry,
702 unsigned short row)
703{
704 char s[256];
Jiri Olsa12400052012-10-13 00:06:16 +0200705 int printed = 0;
Namhyung Kim67d25912012-09-12 15:35:06 +0900706 int width = browser->b.width;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300707 char folded_sign = ' ';
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300708 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300709 off_t row_offset = entry->row_offset;
Namhyung Kim63a1a3d2012-10-15 18:14:35 -0300710 bool first = true;
Jiri Olsa12400052012-10-13 00:06:16 +0200711 struct perf_hpp_fmt *fmt;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300712
713 if (current_entry) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300714 browser->he_selection = entry;
715 browser->selection = &entry->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300716 }
717
718 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300719 hist_entry__init_have_children(entry);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300720 folded_sign = hist_entry__folded(entry);
721 }
722
723 if (row_offset == 0) {
Namhyung Kim89701462013-01-22 18:09:38 +0900724 struct hpp_arg arg = {
Namhyung Kimfb821c9e2014-03-03 17:05:19 +0900725 .b = &browser->b,
Namhyung Kim89701462013-01-22 18:09:38 +0900726 .folded_sign = folded_sign,
727 .current_entry = current_entry,
728 };
Namhyung Kimf5951d52012-09-03 11:53:09 +0900729 struct perf_hpp hpp = {
730 .buf = s,
731 .size = sizeof(s),
Namhyung Kim89701462013-01-22 18:09:38 +0900732 .ptr = &arg,
Namhyung Kimf5951d52012-09-03 11:53:09 +0900733 };
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300734
Namhyung Kim67d25912012-09-12 15:35:06 +0900735 ui_browser__gotorc(&browser->b, row, 0);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900736
Jiri Olsa12400052012-10-13 00:06:16 +0200737 perf_hpp__for_each_format(fmt) {
Namhyung Kime67d49a2014-03-18 13:00:59 +0900738 if (perf_hpp__should_skip(fmt))
739 continue;
740
Namhyung Kimfb821c9e2014-03-03 17:05:19 +0900741 if (current_entry && browser->b.navkeypressed) {
742 ui_browser__set_color(&browser->b,
743 HE_COLORSET_SELECTED);
744 } else {
745 ui_browser__set_color(&browser->b,
746 HE_COLORSET_NORMAL);
747 }
748
749 if (first) {
750 if (symbol_conf.use_callchain) {
751 slsmg_printf("%c ", folded_sign);
752 width -= 2;
753 }
754 first = false;
755 } else {
Namhyung Kimf5951d52012-09-03 11:53:09 +0900756 slsmg_printf(" ");
757 width -= 2;
758 }
759
Jiri Olsa12400052012-10-13 00:06:16 +0200760 if (fmt->color) {
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100761 width -= fmt->color(fmt, &hpp, entry);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900762 } else {
Jiri Olsa2c5d4b42013-01-31 23:31:11 +0100763 width -= fmt->entry(fmt, &hpp, entry);
Namhyung Kimf5951d52012-09-03 11:53:09 +0900764 slsmg_printf("%s", s);
765 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300766 }
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -0200767
768 /* The scroll bar isn't being used */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300769 if (!browser->b.navkeypressed)
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -0200770 width += 1;
771
Namhyung Kim26d8b332014-03-03 16:16:20 +0900772 slsmg_write_nstring("", width);
773
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300774 ++row;
775 ++printed;
776 } else
777 --row_offset;
778
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300779 if (folded_sign == '-' && row != browser->b.height) {
780 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300781 1, row, &row_offset,
782 &current_entry);
783 if (current_entry)
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300784 browser->he_selection = entry;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300785 }
786
787 return printed;
788}
789
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -0300790static void ui_browser__hists_init_top(struct ui_browser *browser)
791{
792 if (browser->top == NULL) {
793 struct hist_browser *hb;
794
795 hb = container_of(browser, struct hist_browser, b);
796 browser->top = rb_first(&hb->hists->entries);
797 }
798}
799
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300800static unsigned int hist_browser__refresh(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300801{
802 unsigned row = 0;
803 struct rb_node *nd;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300804 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300805
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300806 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300807
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300808 for (nd = browser->top; nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300809 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +0900810 float percent;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300811
812 if (h->filtered)
813 continue;
814
Namhyung Kim14135662013-10-31 10:17:39 +0900815 percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +0900816 if (percent < hb->min_pcnt)
817 continue;
818
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300819 row += hist_browser__show_entry(hb, h, row);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300820 if (row == browser->height)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300821 break;
822 }
823
824 return row;
825}
826
Namhyung Kim064f1982013-05-14 11:09:04 +0900827static struct rb_node *hists__filter_entries(struct rb_node *nd,
Namhyung Kim064f1982013-05-14 11:09:04 +0900828 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300829{
830 while (nd != NULL) {
831 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +0900832 float percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +0900833
Namhyung Kimc0f15272014-04-16 11:16:33 +0900834 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300835 return nd;
836
837 nd = rb_next(nd);
838 }
839
840 return NULL;
841}
842
Namhyung Kim064f1982013-05-14 11:09:04 +0900843static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
Namhyung Kim064f1982013-05-14 11:09:04 +0900844 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300845{
846 while (nd != NULL) {
847 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +0900848 float percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +0900849
850 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300851 return nd;
852
853 nd = rb_prev(nd);
854 }
855
856 return NULL;
857}
858
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300859static void ui_browser__hists_seek(struct ui_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300860 off_t offset, int whence)
861{
862 struct hist_entry *h;
863 struct rb_node *nd;
864 bool first = true;
Namhyung Kim064f1982013-05-14 11:09:04 +0900865 struct hist_browser *hb;
866
867 hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300868
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300869 if (browser->nr_entries == 0)
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -0300870 return;
871
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300872 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -0300873
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300874 switch (whence) {
875 case SEEK_SET:
Namhyung Kim064f1982013-05-14 11:09:04 +0900876 nd = hists__filter_entries(rb_first(browser->entries),
Namhyung Kim14135662013-10-31 10:17:39 +0900877 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300878 break;
879 case SEEK_CUR:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300880 nd = browser->top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300881 goto do_offset;
882 case SEEK_END:
Namhyung Kim064f1982013-05-14 11:09:04 +0900883 nd = hists__filter_prev_entries(rb_last(browser->entries),
Namhyung Kim14135662013-10-31 10:17:39 +0900884 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300885 first = false;
886 break;
887 default:
888 return;
889 }
890
891 /*
892 * Moves not relative to the first visible entry invalidates its
893 * row_offset:
894 */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300895 h = rb_entry(browser->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300896 h->row_offset = 0;
897
898 /*
899 * Here we have to check if nd is expanded (+), if it is we can't go
900 * the next top level hist_entry, instead we must compute an offset of
901 * what _not_ to show and not change the first visible entry.
902 *
903 * This offset increments when we are going from top to bottom and
904 * decreases when we're going from bottom to top.
905 *
906 * As we don't have backpointers to the top level in the callchains
907 * structure, we need to always print the whole hist_entry callchain,
908 * skipping the first ones that are before the first visible entry
909 * and stop when we printed enough lines to fill the screen.
910 */
911do_offset:
912 if (offset > 0) {
913 do {
914 h = rb_entry(nd, struct hist_entry, rb_node);
915 if (h->ms.unfolded) {
916 u16 remaining = h->nr_rows - h->row_offset;
917 if (offset > remaining) {
918 offset -= remaining;
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 }
Namhyung Kim14135662013-10-31 10:17:39 +0900927 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300928 if (nd == NULL)
929 break;
930 --offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300931 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300932 } while (offset != 0);
933 } else if (offset < 0) {
934 while (1) {
935 h = rb_entry(nd, struct hist_entry, rb_node);
936 if (h->ms.unfolded) {
937 if (first) {
938 if (-offset > h->row_offset) {
939 offset += h->row_offset;
940 h->row_offset = 0;
941 } else {
942 h->row_offset += offset;
943 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300944 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300945 break;
946 }
947 } else {
948 if (-offset > h->nr_rows) {
949 offset += h->nr_rows;
950 h->row_offset = 0;
951 } else {
952 h->row_offset = h->nr_rows + offset;
953 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300954 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300955 break;
956 }
957 }
958 }
959
Namhyung Kim14135662013-10-31 10:17:39 +0900960 nd = hists__filter_prev_entries(rb_prev(nd),
Namhyung Kim064f1982013-05-14 11:09:04 +0900961 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300962 if (nd == NULL)
963 break;
964 ++offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300965 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300966 if (offset == 0) {
967 /*
968 * Last unfiltered hist_entry, check if it is
969 * unfolded, if it is then we should have
970 * row_offset at its last entry.
971 */
972 h = rb_entry(nd, struct hist_entry, rb_node);
973 if (h->ms.unfolded)
974 h->row_offset = h->nr_rows;
975 break;
976 }
977 first = false;
978 }
979 } else {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300980 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300981 h = rb_entry(nd, struct hist_entry, rb_node);
982 h->row_offset = 0;
983 }
984}
985
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -0300986static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
987 struct callchain_node *chain_node,
988 u64 total, int level,
989 FILE *fp)
990{
991 struct rb_node *node;
992 int offset = level * LEVEL_OFFSET_STEP;
993 u64 new_total, remaining;
994 int printed = 0;
995
996 if (callchain_param.mode == CHAIN_GRAPH_REL)
997 new_total = chain_node->children_hit;
998 else
999 new_total = total;
1000
1001 remaining = new_total;
1002 node = rb_first(&chain_node->rb_root);
1003 while (node) {
1004 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1005 struct rb_node *next = rb_next(node);
1006 u64 cumul = callchain_cumul_hits(child);
1007 struct callchain_list *chain;
1008 char folded_sign = ' ';
1009 int first = true;
1010 int extra_offset = 0;
1011
1012 remaining -= cumul;
1013
1014 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001015 char bf[1024], *alloc_str;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001016 const char *str;
1017 bool was_first = first;
1018
1019 if (first)
1020 first = false;
1021 else
1022 extra_offset = LEVEL_OFFSET_STEP;
1023
1024 folded_sign = callchain_list__folded(chain);
1025
1026 alloc_str = NULL;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001027 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1028 browser->show_dso);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001029 if (was_first) {
1030 double percent = cumul * 100.0 / new_total;
1031
1032 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1033 str = "Not enough memory!";
1034 else
1035 str = alloc_str;
1036 }
1037
1038 printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
1039 free(alloc_str);
1040 if (folded_sign == '+')
1041 break;
1042 }
1043
1044 if (folded_sign == '-') {
1045 const int new_level = level + (extra_offset ? 2 : 1);
1046 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
1047 new_level, fp);
1048 }
1049
1050 node = next;
1051 }
1052
1053 return printed;
1054}
1055
1056static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
1057 struct callchain_node *node,
1058 int level, FILE *fp)
1059{
1060 struct callchain_list *chain;
1061 int offset = level * LEVEL_OFFSET_STEP;
1062 char folded_sign = ' ';
1063 int printed = 0;
1064
1065 list_for_each_entry(chain, &node->val, list) {
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001066 char bf[1024], *s;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001067
1068 folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001069 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001070 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
1071 }
1072
1073 if (folded_sign == '-')
1074 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
1075 browser->hists->stats.total_period,
1076 level + 1, fp);
1077 return printed;
1078}
1079
1080static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1081 struct rb_root *chain, int level, FILE *fp)
1082{
1083 struct rb_node *nd;
1084 int printed = 0;
1085
1086 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1087 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1088
1089 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
1090 }
1091
1092 return printed;
1093}
1094
1095static int hist_browser__fprintf_entry(struct hist_browser *browser,
1096 struct hist_entry *he, FILE *fp)
1097{
1098 char s[8192];
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001099 int printed = 0;
1100 char folded_sign = ' ';
Namhyung Kim26d8b332014-03-03 16:16:20 +09001101 struct perf_hpp hpp = {
1102 .buf = s,
1103 .size = sizeof(s),
1104 };
1105 struct perf_hpp_fmt *fmt;
1106 bool first = true;
1107 int ret;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001108
1109 if (symbol_conf.use_callchain)
1110 folded_sign = hist_entry__folded(he);
1111
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001112 if (symbol_conf.use_callchain)
1113 printed += fprintf(fp, "%c ", folded_sign);
1114
Namhyung Kim26d8b332014-03-03 16:16:20 +09001115 perf_hpp__for_each_format(fmt) {
Namhyung Kime67d49a2014-03-18 13:00:59 +09001116 if (perf_hpp__should_skip(fmt))
1117 continue;
1118
Namhyung Kim26d8b332014-03-03 16:16:20 +09001119 if (!first) {
1120 ret = scnprintf(hpp.buf, hpp.size, " ");
1121 advance_hpp(&hpp, ret);
1122 } else
1123 first = false;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001124
Namhyung Kim26d8b332014-03-03 16:16:20 +09001125 ret = fmt->entry(fmt, &hpp, he);
1126 advance_hpp(&hpp, ret);
1127 }
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001128 printed += fprintf(fp, "%s\n", rtrim(s));
1129
1130 if (folded_sign == '-')
1131 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1132
1133 return printed;
1134}
1135
1136static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1137{
Namhyung Kim064f1982013-05-14 11:09:04 +09001138 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
Namhyung Kim064f1982013-05-14 11:09:04 +09001139 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001140 int printed = 0;
1141
1142 while (nd) {
1143 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1144
1145 printed += hist_browser__fprintf_entry(browser, h, fp);
Namhyung Kim14135662013-10-31 10:17:39 +09001146 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001147 }
1148
1149 return printed;
1150}
1151
1152static int hist_browser__dump(struct hist_browser *browser)
1153{
1154 char filename[64];
1155 FILE *fp;
1156
1157 while (1) {
1158 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1159 if (access(filename, F_OK))
1160 break;
1161 /*
1162 * XXX: Just an arbitrary lazy upper limit
1163 */
1164 if (++browser->print_seq == 8192) {
1165 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1166 return -1;
1167 }
1168 }
1169
1170 fp = fopen(filename, "w");
1171 if (fp == NULL) {
1172 char bf[64];
Kirill A. Shutemov4cc49d42012-07-24 00:06:54 +03001173 const char *err = strerror_r(errno, bf, sizeof(bf));
1174 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001175 return -1;
1176 }
1177
1178 ++browser->print_seq;
1179 hist_browser__fprintf(browser, fp);
1180 fclose(fp);
1181 ui_helpline__fpush("%s written!", filename);
1182
1183 return 0;
1184}
1185
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001186static struct hist_browser *hist_browser__new(struct hists *hists)
1187{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001188 struct hist_browser *browser = zalloc(sizeof(*browser));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001189
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001190 if (browser) {
1191 browser->hists = hists;
1192 browser->b.refresh = hist_browser__refresh;
1193 browser->b.seek = ui_browser__hists_seek;
1194 browser->b.use_navkeypressed = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001195 }
1196
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001197 return browser;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001198}
1199
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001200static void hist_browser__delete(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001201{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001202 free(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001203}
1204
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001205static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001206{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001207 return browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001208}
1209
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001210static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001211{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001212 return browser->he_selection->thread;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001213}
1214
Jiri Olsadd00d482014-06-19 13:41:13 +02001215static int hists__browser_title(struct hists *hists, char *bf, size_t size)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001216{
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001217 char unit;
1218 int printed;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001219 const struct dso *dso = hists->dso_filter;
1220 const struct thread *thread = hists->thread_filter;
1221 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1222 u64 nr_events = hists->stats.total_period;
Namhyung Kim717e2632013-01-22 18:09:44 +09001223 struct perf_evsel *evsel = hists_to_evsel(hists);
Jiri Olsadd00d482014-06-19 13:41:13 +02001224 const char *ev_name = perf_evsel__name(evsel);
Namhyung Kim717e2632013-01-22 18:09:44 +09001225 char buf[512];
1226 size_t buflen = sizeof(buf);
1227
Namhyung Kimf2148332014-01-14 11:52:48 +09001228 if (symbol_conf.filter_relative) {
1229 nr_samples = hists->stats.nr_non_filtered_samples;
1230 nr_events = hists->stats.total_non_filtered_period;
1231 }
1232
Namhyung Kim759ff492013-03-05 14:53:26 +09001233 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001234 struct perf_evsel *pos;
1235
1236 perf_evsel__group_desc(evsel, buf, buflen);
1237 ev_name = buf;
1238
1239 for_each_group_member(pos, evsel) {
Namhyung Kimf2148332014-01-14 11:52:48 +09001240 if (symbol_conf.filter_relative) {
1241 nr_samples += pos->hists.stats.nr_non_filtered_samples;
1242 nr_events += pos->hists.stats.total_non_filtered_period;
1243 } else {
1244 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1245 nr_events += pos->hists.stats.total_period;
1246 }
Namhyung Kim717e2632013-01-22 18:09:44 +09001247 }
1248 }
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001249
Ashay Ranecc686282012-04-05 21:01:01 -05001250 nr_samples = convert_unit(nr_samples, &unit);
1251 printed = scnprintf(bf, size,
1252 "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1253 nr_samples, unit, ev_name, nr_events);
1254
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001255
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001256 if (hists->uid_filter_str)
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02001257 printed += snprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001258 ", UID: %s", hists->uid_filter_str);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001259 if (thread)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001260 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001261 ", Thread: %s(%d)",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001262 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001263 thread->tid);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001264 if (dso)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001265 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001266 ", DSO: %s", dso->short_name);
1267 return printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001268}
1269
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001270static inline void free_popup_options(char **options, int n)
1271{
1272 int i;
1273
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -03001274 for (i = 0; i < n; ++i)
1275 zfree(&options[i]);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001276}
1277
Feng Tangc77d8d72012-11-01 00:00:55 +08001278/* Check whether the browser is for 'top' or 'report' */
1279static inline bool is_report_browser(void *timer)
1280{
1281 return timer == NULL;
1282}
1283
Feng Tang341487ab2013-02-03 14:38:20 +08001284/*
1285 * Only runtime switching of perf data file will make "input_name" point
1286 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1287 * whether we need to call free() for current "input_name" during the switch.
1288 */
1289static bool is_input_name_malloced = false;
1290
1291static int switch_data_file(void)
1292{
1293 char *pwd, *options[32], *abs_path[32], *tmp;
1294 DIR *pwd_dir;
1295 int nr_options = 0, choice = -1, ret = -1;
1296 struct dirent *dent;
1297
1298 pwd = getenv("PWD");
1299 if (!pwd)
1300 return ret;
1301
1302 pwd_dir = opendir(pwd);
1303 if (!pwd_dir)
1304 return ret;
1305
1306 memset(options, 0, sizeof(options));
1307 memset(options, 0, sizeof(abs_path));
1308
1309 while ((dent = readdir(pwd_dir))) {
1310 char path[PATH_MAX];
1311 u64 magic;
1312 char *name = dent->d_name;
1313 FILE *file;
1314
1315 if (!(dent->d_type == DT_REG))
1316 continue;
1317
1318 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1319
1320 file = fopen(path, "r");
1321 if (!file)
1322 continue;
1323
1324 if (fread(&magic, 1, 8, file) < 8)
1325 goto close_file_and_continue;
1326
1327 if (is_perf_magic(magic)) {
1328 options[nr_options] = strdup(name);
1329 if (!options[nr_options])
1330 goto close_file_and_continue;
1331
1332 abs_path[nr_options] = strdup(path);
1333 if (!abs_path[nr_options]) {
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -03001334 zfree(&options[nr_options]);
Feng Tang341487ab2013-02-03 14:38:20 +08001335 ui__warning("Can't search all data files due to memory shortage.\n");
1336 fclose(file);
1337 break;
1338 }
1339
1340 nr_options++;
1341 }
1342
1343close_file_and_continue:
1344 fclose(file);
1345 if (nr_options >= 32) {
1346 ui__warning("Too many perf data files in PWD!\n"
1347 "Only the first 32 files will be listed.\n");
1348 break;
1349 }
1350 }
1351 closedir(pwd_dir);
1352
1353 if (nr_options) {
1354 choice = ui__popup_menu(nr_options, options);
1355 if (choice < nr_options && choice >= 0) {
1356 tmp = strdup(abs_path[choice]);
1357 if (tmp) {
1358 if (is_input_name_malloced)
1359 free((void *)input_name);
1360 input_name = tmp;
1361 is_input_name_malloced = true;
1362 ret = 0;
1363 } else
1364 ui__warning("Data switch failed due to memory shortage!\n");
1365 }
1366 }
1367
1368 free_popup_options(options, nr_options);
1369 free_popup_options(abs_path, nr_options);
1370 return ret;
1371}
1372
Namhyung Kim112f7612014-04-22 14:05:35 +09001373static void hist_browser__update_nr_entries(struct hist_browser *hb)
Namhyung Kim064f1982013-05-14 11:09:04 +09001374{
1375 u64 nr_entries = 0;
1376 struct rb_node *nd = rb_first(&hb->hists->entries);
1377
Namhyung Kim268397c2014-04-22 14:49:31 +09001378 if (hb->min_pcnt == 0) {
1379 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1380 return;
1381 }
1382
Namhyung Kim14135662013-10-31 10:17:39 +09001383 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
Namhyung Kim064f1982013-05-14 11:09:04 +09001384 nr_entries++;
Namhyung Kimc481f932014-04-22 13:56:11 +09001385 nd = rb_next(nd);
Namhyung Kim064f1982013-05-14 11:09:04 +09001386 }
1387
Namhyung Kim112f7612014-04-22 14:05:35 +09001388 hb->nr_non_filtered_entries = nr_entries;
Namhyung Kim064f1982013-05-14 11:09:04 +09001389}
Feng Tang341487ab2013-02-03 14:38:20 +08001390
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001391static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
Jiri Olsadd00d482014-06-19 13:41:13 +02001392 const char *helpline,
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001393 bool left_exits,
Namhyung Kim68d80752012-11-02 14:50:06 +09001394 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001395 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001396 struct perf_session_env *env)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001397{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001398 struct hists *hists = &evsel->hists;
1399 struct hist_browser *browser = hist_browser__new(hists);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001400 struct branch_info *bi;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001401 struct pstack *fstack;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001402 char *options[16];
1403 int nr_options = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001404 int key = -1;
Namhyung Kim938a23a2012-03-16 17:50:53 +09001405 char buf[64];
Feng Tangcdbab7c2012-10-30 11:56:06 +08001406 char script_opt[64];
Namhyung Kim9783adf2012-11-02 14:50:05 +09001407 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001408
Namhyung Kime8e684a2013-12-26 14:37:58 +09001409#define HIST_BROWSER_HELP_COMMON \
1410 "h/?/F1 Show this window\n" \
1411 "UP/DOWN/PGUP\n" \
1412 "PGDN/SPACE Navigate\n" \
1413 "q/ESC/CTRL+C Exit browser\n\n" \
1414 "For multiple event sessions:\n\n" \
1415 "TAB/UNTAB Switch events\n\n" \
1416 "For symbolic views (--sort has sym):\n\n" \
1417 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1418 "<- Zoom out\n" \
1419 "a Annotate current symbol\n" \
1420 "C Collapse all callchains\n" \
1421 "d Zoom into current DSO\n" \
1422 "E Expand all callchains\n" \
Namhyung Kim105eb302014-02-10 11:20:10 +09001423 "F Toggle percentage of filtered entries\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09001424
1425 /* help messages are sorted by lexical order of the hotkey */
1426 const char report_help[] = HIST_BROWSER_HELP_COMMON
Namhyung Kim6dd60132013-12-26 14:37:59 +09001427 "i Show header information\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09001428 "P Print histograms to perf.hist.N\n"
1429 "r Run available scripts\n"
1430 "s Switch to another data file in PWD\n"
1431 "t Zoom into current Thread\n"
1432 "V Verbose (DSO names in callchains, etc)\n"
1433 "/ Filter symbol by name";
1434 const char top_help[] = HIST_BROWSER_HELP_COMMON
1435 "P Print histograms to perf.hist.N\n"
1436 "t Zoom into current Thread\n"
1437 "V Verbose (DSO names in callchains, etc)\n"
1438 "/ Filter symbol by name";
1439
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001440 if (browser == NULL)
1441 return -1;
1442
Namhyung Kim064f1982013-05-14 11:09:04 +09001443 if (min_pcnt) {
1444 browser->min_pcnt = min_pcnt;
Namhyung Kim112f7612014-04-22 14:05:35 +09001445 hist_browser__update_nr_entries(browser);
Namhyung Kim064f1982013-05-14 11:09:04 +09001446 }
1447
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001448 fstack = pstack__new(2);
1449 if (fstack == NULL)
1450 goto out;
1451
1452 ui_helpline__push(helpline);
1453
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001454 memset(options, 0, sizeof(options));
1455
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001456 while (1) {
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001457 const struct thread *thread = NULL;
1458 const struct dso *dso = NULL;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001459 int choice = 0,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001460 annotate = -2, zoom_dso = -2, zoom_thread = -2,
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001461 annotate_f = -2, annotate_t = -2, browse_map = -2;
Feng Tang341487ab2013-02-03 14:38:20 +08001462 int scripts_comm = -2, scripts_symbol = -2,
1463 scripts_all = -2, switch_data = -2;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001464
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001465 nr_options = 0;
1466
Jiri Olsadd00d482014-06-19 13:41:13 +02001467 key = hist_browser__run(browser, hbt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001468
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001469 if (browser->he_selection != NULL) {
1470 thread = hist_browser__selected_thread(browser);
1471 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1472 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001473 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001474 case K_TAB:
1475 case K_UNTAB:
David Aherne4419b82011-10-19 11:37:47 -06001476 if (nr_events == 1)
1477 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001478 /*
1479 * Exit the browser, let hists__browser_tree
1480 * go to the next or previous
1481 */
1482 goto out_free_stack;
1483 case 'a':
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03001484 if (!sort__has_sym) {
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001485 ui_browser__warning(&browser->b, delay_secs * 2,
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02001486 "Annotation is only available for symbolic views, "
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001487 "include \"sym*\" in --sort to use it.");
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02001488 continue;
1489 }
1490
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001491 if (browser->selection == NULL ||
Lin Mingdb9a9cbc2011-04-08 14:31:26 +08001492 browser->selection->sym == NULL ||
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001493 browser->selection->map->dso->annotate_warned)
1494 continue;
1495 goto do_annotate;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001496 case 'P':
1497 hist_browser__dump(browser);
1498 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001499 case 'd':
1500 goto zoom_dso;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03001501 case 'V':
1502 browser->show_dso = !browser->show_dso;
1503 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001504 case 't':
1505 goto zoom_thread;
Arnaldo Carvalho de Melo5a5626b2012-05-09 12:21:22 -03001506 case '/':
Namhyung Kim938a23a2012-03-16 17:50:53 +09001507 if (ui_browser__input_window("Symbol to show",
1508 "Please enter the name of symbol you want to see",
1509 buf, "ENTER: OK, ESC: Cancel",
1510 delay_secs * 2) == K_ENTER) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001511 hists->symbol_filter_str = *buf ? buf : NULL;
1512 hists__filter_by_symbol(hists);
Namhyung Kim938a23a2012-03-16 17:50:53 +09001513 hist_browser__reset(browser);
1514 }
1515 continue;
Feng Tangcdbab7c2012-10-30 11:56:06 +08001516 case 'r':
Namhyung Kim9783adf2012-11-02 14:50:05 +09001517 if (is_report_browser(hbt))
Feng Tangc77d8d72012-11-01 00:00:55 +08001518 goto do_scripts;
1519 continue;
Feng Tang341487ab2013-02-03 14:38:20 +08001520 case 's':
1521 if (is_report_browser(hbt))
1522 goto do_data_switch;
1523 continue;
Namhyung Kim6dd60132013-12-26 14:37:59 +09001524 case 'i':
1525 /* env->arch is NULL for live-mode (i.e. perf top) */
1526 if (env->arch)
1527 tui__header_window(env);
1528 continue;
Namhyung Kim105eb302014-02-10 11:20:10 +09001529 case 'F':
1530 symbol_conf.filter_relative ^= 1;
1531 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001532 case K_F1:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001533 case 'h':
1534 case '?':
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001535 ui_browser__help_window(&browser->b,
Namhyung Kime8e684a2013-12-26 14:37:58 +09001536 is_report_browser(hbt) ? report_help : top_help);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001537 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001538 case K_ENTER:
1539 case K_RIGHT:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001540 /* menu */
1541 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001542 case K_LEFT: {
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001543 const void *top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001544
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001545 if (pstack__empty(fstack)) {
1546 /*
1547 * Go back to the perf_evsel_menu__run or other user
1548 */
1549 if (left_exits)
1550 goto out_free_stack;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001551 continue;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001552 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001553 top = pstack__pop(fstack);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001554 if (top == &browser->hists->dso_filter)
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001555 goto zoom_out_dso;
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001556 if (top == &browser->hists->thread_filter)
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001557 goto zoom_out_thread;
1558 continue;
1559 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001560 case K_ESC:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001561 if (!left_exits &&
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001562 !ui_browser__dialog_yesno(&browser->b,
1563 "Do you really want to exit?"))
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001564 continue;
1565 /* Fall thru */
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001566 case 'q':
1567 case CTRL('c'):
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03001568 goto out_free_stack;
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001569 default:
1570 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001571 }
1572
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03001573 if (!sort__has_sym)
Arnaldo Carvalho de Melo724c9c92011-10-06 10:36:35 -03001574 goto add_exit_option;
1575
Namhyung Kim55369fc2013-04-01 20:35:20 +09001576 if (sort__mode == SORT_MODE__BRANCH) {
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001577 bi = browser->he_selection->branch_info;
1578 if (browser->selection != NULL &&
1579 bi &&
1580 bi->from.sym != NULL &&
1581 !bi->from.map->dso->annotate_warned &&
1582 asprintf(&options[nr_options], "Annotate %s",
1583 bi->from.sym->name) > 0)
1584 annotate_f = nr_options++;
1585
1586 if (browser->selection != NULL &&
1587 bi &&
1588 bi->to.sym != NULL &&
1589 !bi->to.map->dso->annotate_warned &&
Stephane Eranian8bcd65f2012-03-12 16:13:29 +01001590 (bi->to.sym != bi->from.sym ||
1591 bi->to.map->dso != bi->from.map->dso) &&
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001592 asprintf(&options[nr_options], "Annotate %s",
1593 bi->to.sym->name) > 0)
1594 annotate_t = nr_options++;
1595 } else {
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001596 if (browser->selection != NULL &&
1597 browser->selection->sym != NULL &&
Jiri Olsad7553302014-06-15 10:22:15 +02001598 !browser->selection->map->dso->annotate_warned) {
1599 struct annotation *notes;
1600
1601 notes = symbol__annotation(browser->selection->sym);
1602
1603 if (notes->src &&
1604 asprintf(&options[nr_options], "Annotate %s",
1605 browser->selection->sym->name) > 0)
1606 annotate = nr_options++;
1607 }
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001608 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001609
1610 if (thread != NULL &&
1611 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001612 (browser->hists->thread_filter ? "out of" : "into"),
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001613 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001614 thread->tid) > 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001615 zoom_thread = nr_options++;
1616
1617 if (dso != NULL &&
1618 asprintf(&options[nr_options], "Zoom %s %s DSO",
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001619 (browser->hists->dso_filter ? "out of" : "into"),
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001620 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1621 zoom_dso = nr_options++;
1622
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001623 if (browser->selection != NULL &&
1624 browser->selection->map != NULL &&
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001625 asprintf(&options[nr_options], "Browse map details") > 0)
1626 browse_map = nr_options++;
Feng Tangcdbab7c2012-10-30 11:56:06 +08001627
1628 /* perf script support */
1629 if (browser->he_selection) {
1630 struct symbol *sym;
1631
1632 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001633 thread__comm_str(browser->he_selection->thread)) > 0)
Feng Tangcdbab7c2012-10-30 11:56:06 +08001634 scripts_comm = nr_options++;
1635
1636 sym = browser->he_selection->ms.sym;
1637 if (sym && sym->namelen &&
1638 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1639 sym->name) > 0)
1640 scripts_symbol = nr_options++;
1641 }
1642
1643 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1644 scripts_all = nr_options++;
1645
Feng Tang341487ab2013-02-03 14:38:20 +08001646 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1647 "Switch to another data file in PWD") > 0)
1648 switch_data = nr_options++;
Arnaldo Carvalho de Melo724c9c92011-10-06 10:36:35 -03001649add_exit_option:
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001650 options[nr_options++] = (char *)"Exit";
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001651retry_popup_menu:
Arnaldo Carvalho de Melo1e6dd072010-08-10 15:58:50 -03001652 choice = ui__popup_menu(nr_options, options);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001653
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001654 if (choice == nr_options - 1)
1655 break;
1656
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001657 if (choice == -1) {
1658 free_popup_options(options, nr_options - 1);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001659 continue;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001660 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001661
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001662 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001663 struct hist_entry *he;
Jiri Olsad7553302014-06-15 10:22:15 +02001664 struct annotation *notes;
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001665 int err;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001666do_annotate:
Namhyung Kim68d80752012-11-02 14:50:06 +09001667 if (!objdump_path && perf_session_env__lookup_objdump(env))
1668 continue;
1669
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001670 he = hist_browser__selected_entry(browser);
1671 if (he == NULL)
1672 continue;
Stephane Eraniana68c2c52012-03-08 23:47:48 +01001673
1674 /*
1675 * we stash the branch_info symbol + map into the
1676 * the ms so we don't have to rewrite all the annotation
1677 * code to use branch_info.
1678 * in branch mode, the ms struct is not used
1679 */
1680 if (choice == annotate_f) {
1681 he->ms.sym = he->branch_info->from.sym;
1682 he->ms.map = he->branch_info->from.map;
1683 } else if (choice == annotate_t) {
1684 he->ms.sym = he->branch_info->to.sym;
1685 he->ms.map = he->branch_info->to.map;
1686 }
1687
Jiri Olsad7553302014-06-15 10:22:15 +02001688 notes = symbol__annotation(he->ms.sym);
1689 if (!notes->src)
1690 continue;
1691
Arnaldo Carvalho de Melodf71d952011-10-13 08:01:33 -03001692 /*
1693 * Don't let this be freed, say, by hists__decay_entry.
1694 */
1695 he->used = true;
Namhyung Kimdb8fd072013-03-05 14:53:21 +09001696 err = hist_entry__tui_annotate(he, evsel, hbt);
Arnaldo Carvalho de Melodf71d952011-10-13 08:01:33 -03001697 he->used = false;
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001698 /*
1699 * offer option to annotate the other branch source or target
1700 * (if they exists) when returning from annotate
1701 */
1702 if ((err == 'q' || err == CTRL('c'))
1703 && annotate_t != -2 && annotate_f != -2)
1704 goto retry_popup_menu;
1705
Arnaldo Carvalho de Melo900e14a2011-10-11 16:15:39 -03001706 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001707 if (err)
1708 ui_browser__handle_resize(&browser->b);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001709
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001710 } else if (choice == browse_map)
1711 map__browse(browser->selection->map);
1712 else if (choice == zoom_dso) {
1713zoom_dso:
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001714 if (browser->hists->dso_filter) {
1715 pstack__remove(fstack, &browser->hists->dso_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001716zoom_out_dso:
1717 ui_helpline__pop();
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001718 browser->hists->dso_filter = NULL;
Jiri Olsaf2998422014-05-23 17:15:47 +02001719 perf_hpp__set_elide(HISTC_DSO, false);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001720 } else {
1721 if (dso == NULL)
1722 continue;
1723 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1724 dso->kernel ? "the Kernel" : dso->short_name);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001725 browser->hists->dso_filter = dso;
Jiri Olsaf2998422014-05-23 17:15:47 +02001726 perf_hpp__set_elide(HISTC_DSO, true);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001727 pstack__push(fstack, &browser->hists->dso_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001728 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001729 hists__filter_by_dso(hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001730 hist_browser__reset(browser);
1731 } else if (choice == zoom_thread) {
1732zoom_thread:
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001733 if (browser->hists->thread_filter) {
1734 pstack__remove(fstack, &browser->hists->thread_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001735zoom_out_thread:
1736 ui_helpline__pop();
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001737 browser->hists->thread_filter = NULL;
Jiri Olsaf2998422014-05-23 17:15:47 +02001738 perf_hpp__set_elide(HISTC_THREAD, false);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001739 } else {
1740 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001741 thread->comm_set ? thread__comm_str(thread) : "",
Adrian Hunter38051232013-07-04 16:20:31 +03001742 thread->tid);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001743 browser->hists->thread_filter = thread;
Jiri Olsaf2998422014-05-23 17:15:47 +02001744 perf_hpp__set_elide(HISTC_THREAD, false);
Arnaldo Carvalho de Melod7b76f02011-10-18 19:07:34 -02001745 pstack__push(fstack, &browser->hists->thread_filter);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001746 }
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001747 hists__filter_by_thread(hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001748 hist_browser__reset(browser);
1749 }
Feng Tangcdbab7c2012-10-30 11:56:06 +08001750 /* perf scripts support */
1751 else if (choice == scripts_all || choice == scripts_comm ||
1752 choice == scripts_symbol) {
1753do_scripts:
1754 memset(script_opt, 0, 64);
1755
1756 if (choice == scripts_comm)
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001757 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
Feng Tangcdbab7c2012-10-30 11:56:06 +08001758
1759 if (choice == scripts_symbol)
1760 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1761
1762 script_browse(script_opt);
1763 }
Feng Tang341487ab2013-02-03 14:38:20 +08001764 /* Switch to another data file */
1765 else if (choice == switch_data) {
1766do_data_switch:
1767 if (!switch_data_file()) {
1768 key = K_SWITCH_INPUT_DATA;
1769 break;
1770 } else
1771 ui__warning("Won't switch the data files due to\n"
1772 "no valid data file get selected!\n");
1773 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001774 }
1775out_free_stack:
1776 pstack__delete(fstack);
1777out:
1778 hist_browser__delete(browser);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001779 free_popup_options(options, nr_options - 1);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001780 return key;
1781}
1782
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001783struct perf_evsel_menu {
1784 struct ui_browser b;
1785 struct perf_evsel *selection;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001786 bool lost_events, lost_events_warned;
Namhyung Kim064f1982013-05-14 11:09:04 +09001787 float min_pcnt;
Namhyung Kim68d80752012-11-02 14:50:06 +09001788 struct perf_session_env *env;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001789};
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001790
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001791static void perf_evsel_menu__write(struct ui_browser *browser,
1792 void *entry, int row)
1793{
1794 struct perf_evsel_menu *menu = container_of(browser,
1795 struct perf_evsel_menu, b);
1796 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1797 bool current_entry = ui_browser__is_current_entry(browser, row);
1798 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001799 const char *ev_name = perf_evsel__name(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001800 char bf[256], unit;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001801 const char *warn = " ";
1802 size_t printed;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001803
1804 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1805 HE_COLORSET_NORMAL);
1806
Namhyung Kim759ff492013-03-05 14:53:26 +09001807 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001808 struct perf_evsel *pos;
1809
1810 ev_name = perf_evsel__group_name(evsel);
1811
1812 for_each_group_member(pos, evsel) {
1813 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1814 }
1815 }
1816
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001817 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001818 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001819 unit, unit == ' ' ? "" : " ", ev_name);
1820 slsmg_printf("%s", bf);
1821
1822 nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1823 if (nr_events != 0) {
1824 menu->lost_events = true;
1825 if (!current_entry)
1826 ui_browser__set_color(browser, HE_COLORSET_TOP);
1827 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001828 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1829 nr_events, unit, unit == ' ' ? "" : " ");
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001830 warn = bf;
1831 }
1832
1833 slsmg_write_nstring(warn, browser->width - printed);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001834
1835 if (current_entry)
1836 menu->selection = evsel;
1837}
1838
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001839static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1840 int nr_events, const char *help,
Namhyung Kim9783adf2012-11-02 14:50:05 +09001841 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001842{
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001843 struct perf_evlist *evlist = menu->b.priv;
1844 struct perf_evsel *pos;
Jiri Olsadd00d482014-06-19 13:41:13 +02001845 const char *title = "Available samples";
Namhyung Kim9783adf2012-11-02 14:50:05 +09001846 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001847 int key;
1848
1849 if (ui_browser__show(&menu->b, title,
1850 "ESC: exit, ENTER|->: Browse histograms") < 0)
1851 return -1;
1852
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001853 while (1) {
Arnaldo Carvalho de Melo3af6e332011-10-13 08:52:46 -03001854 key = ui_browser__run(&menu->b, delay_secs);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001855
1856 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001857 case K_TIMER:
Namhyung Kim9783adf2012-11-02 14:50:05 +09001858 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02001859
1860 if (!menu->lost_events_warned && menu->lost_events) {
1861 ui_browser__warn_lost_events(&menu->b);
1862 menu->lost_events_warned = true;
1863 }
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001864 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001865 case K_RIGHT:
1866 case K_ENTER:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001867 if (!menu->selection)
1868 continue;
1869 pos = menu->selection;
1870browse_hists:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001871 perf_evlist__set_selected(evlist, pos);
1872 /*
1873 * Give the calling tool a chance to populate the non
1874 * default evsel resorted hists tree.
1875 */
Namhyung Kim9783adf2012-11-02 14:50:05 +09001876 if (hbt)
1877 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03001878 key = perf_evsel__hists_browse(pos, nr_events, help,
Jiri Olsadd00d482014-06-19 13:41:13 +02001879 true, hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001880 menu->min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001881 menu->env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001882 ui_browser__show_title(&menu->b, title);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001883 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001884 case K_TAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001885 if (pos->node.next == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001886 pos = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001887 else
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001888 pos = perf_evsel__next(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001889 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001890 case K_UNTAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001891 if (pos->node.prev == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001892 pos = perf_evlist__last(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001893 else
Arnaldo Carvalho de Melod87fcb42013-11-13 15:56:40 -03001894 pos = perf_evsel__prev(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001895 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001896 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001897 if (!ui_browser__dialog_yesno(&menu->b,
1898 "Do you really want to exit?"))
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001899 continue;
1900 /* Fall thru */
Feng Tang341487ab2013-02-03 14:38:20 +08001901 case K_SWITCH_INPUT_DATA:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001902 case 'q':
1903 case CTRL('c'):
1904 goto out;
1905 default:
1906 continue;
1907 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001908 case K_LEFT:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001909 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02001910 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02001911 if (!ui_browser__dialog_yesno(&menu->b,
1912 "Do you really want to exit?"))
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03001913 continue;
1914 /* Fall thru */
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001915 case 'q':
1916 case CTRL('c'):
1917 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001918 default:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03001919 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001920 }
1921 }
1922
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001923out:
1924 ui_browser__hide(&menu->b);
1925 return key;
1926}
1927
Arnaldo Carvalho de Melo316c7132013-11-05 15:32:36 -03001928static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001929 void *entry)
1930{
1931 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1932
1933 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1934 return true;
1935
1936 return false;
1937}
1938
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001939static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001940 int nr_entries, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001941 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001942 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001943 struct perf_session_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001944{
1945 struct perf_evsel *pos;
1946 struct perf_evsel_menu menu = {
1947 .b = {
1948 .entries = &evlist->entries,
1949 .refresh = ui_browser__list_head_refresh,
1950 .seek = ui_browser__list_head_seek,
1951 .write = perf_evsel_menu__write,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001952 .filter = filter_group_entries,
1953 .nr_entries = nr_entries,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001954 .priv = evlist,
1955 },
Namhyung Kim064f1982013-05-14 11:09:04 +09001956 .min_pcnt = min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001957 .env = env,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001958 };
1959
1960 ui_helpline__push("Press ESC to exit");
1961
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001962 evlist__for_each(evlist, pos) {
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03001963 const char *ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001964 size_t line_len = strlen(ev_name) + 7;
1965
1966 if (menu.b.width < line_len)
1967 menu.b.width = line_len;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001968 }
1969
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001970 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001971}
1972
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03001973int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09001974 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001975 float min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09001976 struct perf_session_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001977{
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001978 int nr_entries = evlist->nr_entries;
1979
1980single_entry:
1981 if (nr_entries == 1) {
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03001982 struct perf_evsel *first = perf_evlist__first(evlist);
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001983
1984 return perf_evsel__hists_browse(first, nr_entries, help,
Jiri Olsadd00d482014-06-19 13:41:13 +02001985 false, hbt, min_pcnt,
Namhyung Kim064f1982013-05-14 11:09:04 +09001986 env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03001987 }
1988
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001989 if (symbol_conf.event_group) {
1990 struct perf_evsel *pos;
1991
1992 nr_entries = 0;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001993 evlist__for_each(evlist, pos) {
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001994 if (perf_evsel__is_group_leader(pos))
1995 nr_entries++;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03001996 }
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09001997
1998 if (nr_entries == 1)
1999 goto single_entry;
2000 }
2001
2002 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09002003 hbt, min_pcnt, env);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002004}