blob: a53fef0c673bbbfcf8669ffb66f5de079dbc7e18 [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 <stdlib.h>
3#include <string.h>
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03004#include <linux/rbtree.h>
5
Namhyung Kimaca7a942012-04-04 00:14:26 -07006#include "../../util/evsel.h"
7#include "../../util/evlist.h"
8#include "../../util/hist.h"
9#include "../../util/pstack.h"
10#include "../../util/sort.h"
11#include "../../util/util.h"
Namhyung Kim42337a22014-08-12 17:16:06 +090012#include "../../util/top.h"
Namhyung Kim68d80752012-11-02 14:50:06 +090013#include "../../arch/common.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030014
Jiri Olsaf7589902016-06-20 23:58:13 +020015#include "../browsers/hists.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030016#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
Namhyung Kimf5951d52012-09-03 11:53:09 +090022extern void hist_browser__init_hpp(void);
23
Jiri Olsa5b91a862016-06-20 23:58:15 +020024static int perf_evsel_browser_title(struct hist_browser *browser,
25 char *bf, size_t size);
Namhyung Kim112f7612014-04-22 14:05:35 +090026static void hist_browser__update_nr_entries(struct hist_browser *hb);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -030027
Namhyung Kimc3b78952014-04-22 15:56:17 +090028static struct rb_node *hists__filter_entries(struct rb_node *nd,
Namhyung Kimc3b78952014-04-22 15:56:17 +090029 float min_pcnt);
30
Namhyung Kim268397c2014-04-22 14:49:31 +090031static bool hist_browser__has_filter(struct hist_browser *hb)
32{
Arnaldo Carvalho de Melo9c0fa8d2015-07-13 08:26:35 -030033 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
Namhyung Kim268397c2014-04-22 14:49:31 +090034}
35
He Kuang4fabf3d2015-03-12 15:21:49 +080036static int hist_browser__get_folding(struct hist_browser *browser)
37{
38 struct rb_node *nd;
39 struct hists *hists = browser->hists;
40 int unfolded_rows = 0;
41
42 for (nd = rb_first(&hists->entries);
43 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
Namhyung Kimf5b763f2016-02-25 00:13:43 +090044 nd = rb_hierarchy_next(nd)) {
He Kuang4fabf3d2015-03-12 15:21:49 +080045 struct hist_entry *he =
46 rb_entry(nd, struct hist_entry, rb_node);
47
Namhyung Kimf5b763f2016-02-25 00:13:43 +090048 if (he->leaf && he->unfolded)
He Kuang4fabf3d2015-03-12 15:21:49 +080049 unfolded_rows += he->nr_rows;
50 }
51 return unfolded_rows;
52}
53
Namhyung Kimc3b78952014-04-22 15:56:17 +090054static u32 hist_browser__nr_entries(struct hist_browser *hb)
55{
56 u32 nr_entries;
57
Namhyung Kimf5b763f2016-02-25 00:13:43 +090058 if (symbol_conf.report_hierarchy)
59 nr_entries = hb->nr_hierarchy_entries;
60 else if (hist_browser__has_filter(hb))
Namhyung Kimc3b78952014-04-22 15:56:17 +090061 nr_entries = hb->nr_non_filtered_entries;
62 else
63 nr_entries = hb->hists->nr_entries;
64
He Kuang4fabf3d2015-03-12 15:21:49 +080065 hb->nr_callchain_rows = hist_browser__get_folding(hb);
Namhyung Kimc3b78952014-04-22 15:56:17 +090066 return nr_entries + hb->nr_callchain_rows;
67}
68
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +020069static void hist_browser__update_rows(struct hist_browser *hb)
70{
71 struct ui_browser *browser = &hb->b;
Jiri Olsaf8e67102016-08-07 17:28:26 +020072 struct hists *hists = hb->hists;
73 struct perf_hpp_list *hpp_list = hists->hpp_list;
74 u16 header_offset, index_row;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +020075
Jiri Olsaf8e67102016-08-07 17:28:26 +020076 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +020077 browser->rows = browser->height - header_offset;
78 /*
79 * Verify if we were at the last line and that line isn't
80 * visibe because we now show the header line(s).
81 */
82 index_row = browser->index - browser->top_idx;
83 if (index_row >= browser->rows)
84 browser->index -= index_row - browser->rows + 1;
85}
86
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -030087static void hist_browser__refresh_dimensions(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030088{
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -030089 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
90
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030091 /* 3 == +/- toggle symbol before actual hist_entry rendering */
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -030092 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
93 /*
94 * FIXME: Just keeping existing behaviour, but this really should be
95 * before updating browser->width, as it will invalidate the
96 * calculation above. Fix this and the fallout in another
97 * changeset.
98 */
99 ui_browser__refresh_dimensions(browser);
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200100 hist_browser__update_rows(hb);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300101}
102
Arnaldo Carvalho de Meloca3ff332014-07-01 16:42:24 -0300103static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
104{
Jiri Olsaf8e67102016-08-07 17:28:26 +0200105 struct hists *hists = browser->hists;
106 struct perf_hpp_list *hpp_list = hists->hpp_list;
107 u16 header_offset;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200108
Jiri Olsaf8e67102016-08-07 17:28:26 +0200109 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200110 ui_browser__gotorc(&browser->b, row + header_offset, column);
Arnaldo Carvalho de Meloca3ff332014-07-01 16:42:24 -0300111}
112
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300113static void hist_browser__reset(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300114{
Namhyung Kimc3b78952014-04-22 15:56:17 +0900115 /*
116 * The hists__remove_entry_filter() already folds non-filtered
117 * entries so we can assume it has 0 callchain rows.
118 */
119 browser->nr_callchain_rows = 0;
120
Namhyung Kim268397c2014-04-22 14:49:31 +0900121 hist_browser__update_nr_entries(browser);
Namhyung Kimc3b78952014-04-22 15:56:17 +0900122 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -0300123 hist_browser__refresh_dimensions(&browser->b);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300124 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300125}
126
127static char tree__folded_sign(bool unfolded)
128{
129 return unfolded ? '-' : '+';
130}
131
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300132static char hist_entry__folded(const struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300133{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900134 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300135}
136
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300137static char callchain_list__folded(const struct callchain_list *cl)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300138{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900139 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300140}
141
Namhyung Kim3698dab2015-05-05 23:55:46 +0900142static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300143{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900144 cl->unfolded = unfold ? cl->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300145}
146
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300147static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300148{
149 int n = 0;
150 struct rb_node *nd;
151
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300152 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300153 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
154 struct callchain_list *chain;
155 char folded_sign = ' '; /* No children */
156
157 list_for_each_entry(chain, &child->val, list) {
158 ++n;
159 /* We need this because we may not have children */
160 folded_sign = callchain_list__folded(chain);
161 if (folded_sign == '+')
162 break;
163 }
164
165 if (folded_sign == '-') /* Have children and they're unfolded */
166 n += callchain_node__count_rows_rb_tree(child);
167 }
168
169 return n;
170}
171
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900172static int callchain_node__count_flat_rows(struct callchain_node *node)
173{
174 struct callchain_list *chain;
175 char folded_sign = 0;
176 int n = 0;
177
178 list_for_each_entry(chain, &node->parent_val, list) {
179 if (!folded_sign) {
180 /* only check first chain list entry */
181 folded_sign = callchain_list__folded(chain);
182 if (folded_sign == '+')
183 return 1;
184 }
185 n++;
186 }
187
188 list_for_each_entry(chain, &node->val, list) {
189 if (!folded_sign) {
190 /* node->parent_val list might be empty */
191 folded_sign = callchain_list__folded(chain);
192 if (folded_sign == '+')
193 return 1;
194 }
195 n++;
196 }
197
198 return n;
199}
200
Namhyung Kim8c430a32015-11-09 14:45:44 +0900201static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
202{
203 return 1;
204}
205
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300206static int callchain_node__count_rows(struct callchain_node *node)
207{
208 struct callchain_list *chain;
209 bool unfolded = false;
210 int n = 0;
211
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900212 if (callchain_param.mode == CHAIN_FLAT)
213 return callchain_node__count_flat_rows(node);
Namhyung Kim8c430a32015-11-09 14:45:44 +0900214 else if (callchain_param.mode == CHAIN_FOLDED)
215 return callchain_node__count_folded_rows(node);
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900216
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300217 list_for_each_entry(chain, &node->val, list) {
218 ++n;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900219 unfolded = chain->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300220 }
221
222 if (unfolded)
223 n += callchain_node__count_rows_rb_tree(node);
224
225 return n;
226}
227
228static int callchain__count_rows(struct rb_root *chain)
229{
230 struct rb_node *nd;
231 int n = 0;
232
233 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
234 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
235 n += callchain_node__count_rows(node);
236 }
237
238 return n;
239}
240
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900241static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
242 bool include_children)
243{
244 int count = 0;
245 struct rb_node *node;
246 struct hist_entry *child;
247
248 if (he->leaf)
249 return callchain__count_rows(&he->sorted_chain);
250
Namhyung Kim79dded82016-02-26 21:13:19 +0900251 if (he->has_no_entry)
252 return 1;
253
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900254 node = rb_first(&he->hroot_out);
255 while (node) {
256 float percent;
257
258 child = rb_entry(node, struct hist_entry, rb_node);
259 percent = hist_entry__get_percent_limit(child);
260
261 if (!child->filtered && percent >= hb->min_pcnt) {
262 count++;
263
264 if (include_children && child->unfolded)
265 count += hierarchy_count_rows(hb, child, true);
266 }
267
268 node = rb_next(node);
269 }
270 return count;
271}
272
Namhyung Kim3698dab2015-05-05 23:55:46 +0900273static bool hist_entry__toggle_fold(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300274{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900275 if (!he)
Jiri Olsa8493fe12012-04-04 22:21:31 +0200276 return false;
277
Namhyung Kim3698dab2015-05-05 23:55:46 +0900278 if (!he->has_children)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300279 return false;
280
Namhyung Kim3698dab2015-05-05 23:55:46 +0900281 he->unfolded = !he->unfolded;
282 return true;
283}
284
285static bool callchain_list__toggle_fold(struct callchain_list *cl)
286{
287 if (!cl)
288 return false;
289
290 if (!cl->has_children)
291 return false;
292
293 cl->unfolded = !cl->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300294 return true;
295}
296
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300297static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300298{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300299 struct rb_node *nd = rb_first(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300300
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300301 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300302 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
303 struct callchain_list *chain;
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300304 bool first = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300305
306 list_for_each_entry(chain, &child->val, list) {
307 if (first) {
308 first = false;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900309 chain->has_children = chain->list.next != &child->val ||
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300310 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300311 } else
Namhyung Kim3698dab2015-05-05 23:55:46 +0900312 chain->has_children = chain->list.next == &child->val &&
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300313 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300314 }
315
316 callchain_node__init_have_children_rb_tree(child);
317 }
318}
319
Namhyung Kima7444af2014-11-24 17:13:27 +0900320static void callchain_node__init_have_children(struct callchain_node *node,
321 bool has_sibling)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300322{
323 struct callchain_list *chain;
324
Namhyung Kima7444af2014-11-24 17:13:27 +0900325 chain = list_entry(node->val.next, struct callchain_list, list);
Namhyung Kim3698dab2015-05-05 23:55:46 +0900326 chain->has_children = has_sibling;
Namhyung Kima7444af2014-11-24 17:13:27 +0900327
Andres Freund90989032016-03-30 21:02:45 +0200328 if (!list_empty(&node->val)) {
Namhyung Kim82162b52014-08-13 15:02:41 +0900329 chain = list_entry(node->val.prev, struct callchain_list, list);
Namhyung Kim3698dab2015-05-05 23:55:46 +0900330 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
Namhyung Kim82162b52014-08-13 15:02:41 +0900331 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300332
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300333 callchain_node__init_have_children_rb_tree(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300334}
335
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300336static void callchain__init_have_children(struct rb_root *root)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300337{
Namhyung Kima7444af2014-11-24 17:13:27 +0900338 struct rb_node *nd = rb_first(root);
339 bool has_sibling = nd && rb_next(nd);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300340
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300341 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300342 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
Namhyung Kima7444af2014-11-24 17:13:27 +0900343 callchain_node__init_have_children(node, has_sibling);
Namhyung Kim8c430a32015-11-09 14:45:44 +0900344 if (callchain_param.mode == CHAIN_FLAT ||
345 callchain_param.mode == CHAIN_FOLDED)
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900346 callchain_node__make_parent_list(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300347 }
348}
349
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300350static void hist_entry__init_have_children(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300351{
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900352 if (he->init_have_children)
353 return;
354
355 if (he->leaf) {
Namhyung Kim3698dab2015-05-05 23:55:46 +0900356 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300357 callchain__init_have_children(&he->sorted_chain);
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900358 } else {
359 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300360 }
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900361
362 he->init_have_children = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300363}
364
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300365static bool hist_browser__toggle_fold(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300366{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900367 struct hist_entry *he = browser->he_selection;
368 struct map_symbol *ms = browser->selection;
369 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
370 bool has_children;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300371
Wang Nan4938cf02015-12-07 02:35:44 +0000372 if (!he || !ms)
373 return false;
374
Namhyung Kim3698dab2015-05-05 23:55:46 +0900375 if (ms == &he->ms)
376 has_children = hist_entry__toggle_fold(he);
377 else
378 has_children = callchain_list__toggle_fold(cl);
379
380 if (has_children) {
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900381 int child_rows = 0;
382
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300383 hist_entry__init_have_children(he);
Namhyung Kimc3b78952014-04-22 15:56:17 +0900384 browser->b.nr_entries -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300385
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900386 if (he->leaf)
387 browser->nr_callchain_rows -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300388 else
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900389 browser->nr_hierarchy_entries -= he->nr_rows;
390
391 if (symbol_conf.report_hierarchy)
392 child_rows = hierarchy_count_rows(browser, he, true);
393
394 if (he->unfolded) {
395 if (he->leaf)
396 he->nr_rows = callchain__count_rows(&he->sorted_chain);
397 else
398 he->nr_rows = hierarchy_count_rows(browser, he, false);
399
400 /* account grand children */
401 if (symbol_conf.report_hierarchy)
402 browser->b.nr_entries += child_rows - he->nr_rows;
Namhyung Kim79dded82016-02-26 21:13:19 +0900403
404 if (!he->leaf && he->nr_rows == 0) {
405 he->has_no_entry = true;
406 he->nr_rows = 1;
407 }
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900408 } else {
409 if (symbol_conf.report_hierarchy)
410 browser->b.nr_entries -= child_rows - he->nr_rows;
411
Namhyung Kim79dded82016-02-26 21:13:19 +0900412 if (he->has_no_entry)
413 he->has_no_entry = false;
414
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300415 he->nr_rows = 0;
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900416 }
Namhyung Kimc3b78952014-04-22 15:56:17 +0900417
418 browser->b.nr_entries += he->nr_rows;
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900419
420 if (he->leaf)
421 browser->nr_callchain_rows += he->nr_rows;
422 else
423 browser->nr_hierarchy_entries += he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300424
425 return true;
426 }
427
428 /* If it doesn't have children, no toggling performed */
429 return false;
430}
431
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300432static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300433{
434 int n = 0;
435 struct rb_node *nd;
436
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300437 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
439 struct callchain_list *chain;
440 bool has_children = false;
441
442 list_for_each_entry(chain, &child->val, list) {
443 ++n;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900444 callchain_list__set_folding(chain, unfold);
445 has_children = chain->has_children;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300446 }
447
448 if (has_children)
449 n += callchain_node__set_folding_rb_tree(child, unfold);
450 }
451
452 return n;
453}
454
455static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
456{
457 struct callchain_list *chain;
458 bool has_children = false;
459 int n = 0;
460
461 list_for_each_entry(chain, &node->val, list) {
462 ++n;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900463 callchain_list__set_folding(chain, unfold);
464 has_children = chain->has_children;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300465 }
466
467 if (has_children)
468 n += callchain_node__set_folding_rb_tree(node, unfold);
469
470 return n;
471}
472
473static int callchain__set_folding(struct rb_root *chain, bool unfold)
474{
475 struct rb_node *nd;
476 int n = 0;
477
478 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
479 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
480 n += callchain_node__set_folding(node, unfold);
481 }
482
483 return n;
484}
485
Namhyung Kim492b1012016-02-25 00:13:44 +0900486static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
487 bool unfold __maybe_unused)
488{
489 float percent;
490 struct rb_node *nd;
491 struct hist_entry *child;
492 int n = 0;
493
494 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
495 child = rb_entry(nd, struct hist_entry, rb_node);
496 percent = hist_entry__get_percent_limit(child);
497 if (!child->filtered && percent >= hb->min_pcnt)
498 n++;
499 }
500
501 return n;
502}
503
504static void hist_entry__set_folding(struct hist_entry *he,
505 struct hist_browser *hb, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300506{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300507 hist_entry__init_have_children(he);
Namhyung Kim3698dab2015-05-05 23:55:46 +0900508 he->unfolded = unfold ? he->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300509
Namhyung Kim3698dab2015-05-05 23:55:46 +0900510 if (he->has_children) {
Namhyung Kim492b1012016-02-25 00:13:44 +0900511 int n;
512
513 if (he->leaf)
514 n = callchain__set_folding(&he->sorted_chain, unfold);
515 else
516 n = hierarchy_set_folding(hb, he, unfold);
517
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300518 he->nr_rows = unfold ? n : 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300519 } else
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300520 he->nr_rows = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300521}
522
Namhyung Kimc3b78952014-04-22 15:56:17 +0900523static void
524__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300525{
526 struct rb_node *nd;
Namhyung Kim492b1012016-02-25 00:13:44 +0900527 struct hist_entry *he;
528 double percent;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300529
Namhyung Kim492b1012016-02-25 00:13:44 +0900530 nd = rb_first(&browser->hists->entries);
531 while (nd) {
532 he = rb_entry(nd, struct hist_entry, rb_node);
533
534 /* set folding state even if it's currently folded */
535 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
536
537 hist_entry__set_folding(he, browser, unfold);
538
539 percent = hist_entry__get_percent_limit(he);
540 if (he->filtered || percent < browser->min_pcnt)
541 continue;
542
543 if (!he->depth || unfold)
544 browser->nr_hierarchy_entries++;
545 if (he->leaf)
546 browser->nr_callchain_rows += he->nr_rows;
Namhyung Kim79dded82016-02-26 21:13:19 +0900547 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
548 browser->nr_hierarchy_entries++;
549 he->has_no_entry = true;
550 he->nr_rows = 1;
551 } else
552 he->has_no_entry = false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300553 }
554}
555
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300556static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300557{
Namhyung Kim492b1012016-02-25 00:13:44 +0900558 browser->nr_hierarchy_entries = 0;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900559 browser->nr_callchain_rows = 0;
560 __hist_browser__set_folding(browser, unfold);
561
562 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300563 /* Go to the start, we may be way after valid entries after a collapse */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300564 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300565}
566
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200567static void ui_browser__warn_lost_events(struct ui_browser *browser)
568{
569 ui_browser__warning(browser, 4,
570 "Events are being lost, check IO/CPU overload!\n\n"
571 "You may want to run 'perf' using a RT scheduler policy:\n\n"
572 " perf top -r 80\n\n"
573 "Or reduce the sampling frequency.");
574}
575
Jiri Olsa5b91a862016-06-20 23:58:15 +0200576static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
577{
578 return browser->title ? browser->title(browser, bf, size) : 0;
579}
580
Jiri Olsadabd2012016-06-20 23:58:14 +0200581int hist_browser__run(struct hist_browser *browser, const char *help)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300582{
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300583 int key;
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300584 char title[160];
Namhyung Kimc2a51ab2015-04-22 16:18:15 +0900585 struct hist_browser_timer *hbt = browser->hbt;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900586 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300587
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300588 browser->b.entries = &browser->hists->entries;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900589 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300590
Jiri Olsa5b91a862016-06-20 23:58:15 +0200591 hist_browser__title(browser, title, sizeof(title));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300592
Namhyung Kim090cff32016-01-11 19:53:14 +0900593 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300594 return -1;
595
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300596 while (1) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300597 key = ui_browser__run(&browser->b, delay_secs);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300598
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300599 switch (key) {
Namhyung Kimfa5df942013-05-14 11:09:05 +0900600 case K_TIMER: {
601 u64 nr_entries;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900602 hbt->timer(hbt->arg);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900603
Namhyung Kimc6111522016-10-07 14:04:12 +0900604 if (hist_browser__has_filter(browser) ||
605 symbol_conf.report_hierarchy)
Namhyung Kim112f7612014-04-22 14:05:35 +0900606 hist_browser__update_nr_entries(browser);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900607
Namhyung Kimc3b78952014-04-22 15:56:17 +0900608 nr_entries = hist_browser__nr_entries(browser);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900609 ui_browser__update_nr_entries(&browser->b, nr_entries);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200610
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300611 if (browser->hists->stats.nr_lost_warned !=
612 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
613 browser->hists->stats.nr_lost_warned =
614 browser->hists->stats.nr_events[PERF_RECORD_LOST];
615 ui_browser__warn_lost_events(&browser->b);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200616 }
617
Jiri Olsa5b91a862016-06-20 23:58:15 +0200618 hist_browser__title(browser, title, sizeof(title));
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300619 ui_browser__show_title(&browser->b, title);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300620 continue;
Namhyung Kimfa5df942013-05-14 11:09:05 +0900621 }
Arnaldo Carvalho de Melo46941532010-08-10 15:50:07 -0300622 case 'D': { /* Debug */
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300623 static int seq;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300624 struct hist_entry *h = rb_entry(browser->b.top,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300625 struct hist_entry, rb_node);
626 ui_helpline__pop();
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -0300627 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300628 seq++, browser->b.nr_entries,
629 browser->hists->nr_entries,
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -0300630 browser->b.rows,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300631 browser->b.index,
632 browser->b.top_idx,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300633 h->row_offset, h->nr_rows);
634 }
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300635 break;
636 case 'C':
637 /* Collapse the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300638 hist_browser__set_folding(browser, false);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300639 break;
640 case 'E':
641 /* Expand the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300642 hist_browser__set_folding(browser, true);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300643 break;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200644 case 'H':
645 browser->show_headers = !browser->show_headers;
646 hist_browser__update_rows(browser);
647 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -0200648 case K_ENTER:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300649 if (hist_browser__toggle_fold(browser))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300650 break;
651 /* fall thru */
652 default:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300653 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300654 }
655 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300656out:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300657 ui_browser__hide(&browser->b);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300658 return key;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300659}
660
Namhyung Kim39ee5332014-08-22 09:13:21 +0900661struct callchain_print_arg {
662 /* for hists browser */
663 off_t row_offset;
664 bool is_current_entry;
665
666 /* for file dump */
667 FILE *fp;
668 int printed;
669};
670
671typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
672 struct callchain_list *chain,
673 const char *str, int offset,
674 unsigned short row,
675 struct callchain_print_arg *arg);
676
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900677static void hist_browser__show_callchain_entry(struct hist_browser *browser,
678 struct callchain_list *chain,
Namhyung Kim39ee5332014-08-22 09:13:21 +0900679 const char *str, int offset,
680 unsigned short row,
681 struct callchain_print_arg *arg)
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900682{
683 int color, width;
Namhyung Kim39ee5332014-08-22 09:13:21 +0900684 char folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Melo70e97272015-03-19 16:07:21 -0300685 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900686
687 color = HE_COLORSET_NORMAL;
688 width = browser->b.width - (offset + 2);
689 if (ui_browser__is_current_entry(&browser->b, row)) {
690 browser->selection = &chain->ms;
691 color = HE_COLORSET_SELECTED;
Namhyung Kim39ee5332014-08-22 09:13:21 +0900692 arg->is_current_entry = true;
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900693 }
694
695 ui_browser__set_color(&browser->b, color);
696 hist_browser__gotorc(browser, row, 0);
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -0300697 ui_browser__write_nstring(&browser->b, " ", offset);
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -0300698 ui_browser__printf(&browser->b, "%c", folded_sign);
Arnaldo Carvalho de Melo70e97272015-03-19 16:07:21 -0300699 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -0300700 ui_browser__write_nstring(&browser->b, str, width);
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900701}
702
Namhyung Kim39ee5332014-08-22 09:13:21 +0900703static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
704 struct callchain_list *chain,
705 const char *str, int offset,
706 unsigned short row __maybe_unused,
707 struct callchain_print_arg *arg)
708{
709 char folded_sign = callchain_list__folded(chain);
710
711 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
712 folded_sign, str);
713}
714
715typedef bool (*check_output_full_fn)(struct hist_browser *browser,
716 unsigned short row);
717
718static bool hist_browser__check_output_full(struct hist_browser *browser,
719 unsigned short row)
720{
721 return browser->b.rows == row;
722}
723
724static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
725 unsigned short row __maybe_unused)
726{
727 return false;
728}
729
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300730#define LEVEL_OFFSET_STEP 3
731
Namhyung Kim18bb8382015-11-09 14:45:42 +0900732static int hist_browser__show_callchain_list(struct hist_browser *browser,
733 struct callchain_node *node,
734 struct callchain_list *chain,
735 unsigned short row, u64 total,
736 bool need_percent, int offset,
737 print_callchain_entry_fn print,
738 struct callchain_print_arg *arg)
739{
740 char bf[1024], *alloc_str;
741 const char *str;
742
743 if (arg->row_offset != 0) {
744 arg->row_offset--;
745 return 0;
746 }
747
748 alloc_str = NULL;
749 str = callchain_list__sym_name(chain, bf, sizeof(bf),
750 browser->show_dso);
751
752 if (need_percent) {
753 char buf[64];
754
755 callchain_node__scnprintf_value(node, buf, sizeof(buf),
756 total);
757
758 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
759 str = "Not enough memory!";
760 else
761 str = alloc_str;
762 }
763
764 print(browser, chain, str, offset, row, arg);
765
766 free(alloc_str);
767 return 1;
768}
769
Namhyung Kim59c624e2016-01-28 00:40:56 +0900770static bool check_percent_display(struct rb_node *node, u64 parent_total)
771{
772 struct callchain_node *child;
773
774 if (node == NULL)
775 return false;
776
777 if (rb_next(node))
778 return true;
779
780 child = rb_entry(node, struct callchain_node, rb_node);
781 return callchain_cumul_hits(child) != parent_total;
782}
783
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900784static int hist_browser__show_callchain_flat(struct hist_browser *browser,
785 struct rb_root *root,
786 unsigned short row, u64 total,
Namhyung Kim59c624e2016-01-28 00:40:56 +0900787 u64 parent_total,
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900788 print_callchain_entry_fn print,
789 struct callchain_print_arg *arg,
790 check_output_full_fn is_output_full)
791{
792 struct rb_node *node;
793 int first_row = row, offset = LEVEL_OFFSET_STEP;
794 bool need_percent;
795
796 node = rb_first(root);
Namhyung Kim59c624e2016-01-28 00:40:56 +0900797 need_percent = check_percent_display(node, parent_total);
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900798
799 while (node) {
800 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
801 struct rb_node *next = rb_next(node);
802 struct callchain_list *chain;
803 char folded_sign = ' ';
804 int first = true;
805 int extra_offset = 0;
806
807 list_for_each_entry(chain, &child->parent_val, list) {
808 bool was_first = first;
809
810 if (first)
811 first = false;
812 else if (need_percent)
813 extra_offset = LEVEL_OFFSET_STEP;
814
815 folded_sign = callchain_list__folded(chain);
816
817 row += hist_browser__show_callchain_list(browser, child,
818 chain, row, total,
819 was_first && need_percent,
820 offset + extra_offset,
821 print, arg);
822
823 if (is_output_full(browser, row))
824 goto out;
825
826 if (folded_sign == '+')
827 goto next;
828 }
829
830 list_for_each_entry(chain, &child->val, list) {
831 bool was_first = first;
832
833 if (first)
834 first = false;
835 else if (need_percent)
836 extra_offset = LEVEL_OFFSET_STEP;
837
838 folded_sign = callchain_list__folded(chain);
839
840 row += hist_browser__show_callchain_list(browser, child,
841 chain, row, total,
842 was_first && need_percent,
843 offset + extra_offset,
844 print, arg);
845
846 if (is_output_full(browser, row))
847 goto out;
848
849 if (folded_sign == '+')
850 break;
851 }
852
853next:
854 if (is_output_full(browser, row))
855 break;
856 node = next;
857 }
858out:
859 return row - first_row;
860}
861
Namhyung Kim8c430a32015-11-09 14:45:44 +0900862static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
863 struct callchain_list *chain,
864 char *value_str, char *old_str)
865{
866 char bf[1024];
867 const char *str;
868 char *new;
869
870 str = callchain_list__sym_name(chain, bf, sizeof(bf),
871 browser->show_dso);
872 if (old_str) {
873 if (asprintf(&new, "%s%s%s", old_str,
874 symbol_conf.field_sep ?: ";", str) < 0)
875 new = NULL;
876 } else {
877 if (value_str) {
878 if (asprintf(&new, "%s %s", value_str, str) < 0)
879 new = NULL;
880 } else {
881 if (asprintf(&new, "%s", str) < 0)
882 new = NULL;
883 }
884 }
885 return new;
886}
887
888static int hist_browser__show_callchain_folded(struct hist_browser *browser,
889 struct rb_root *root,
890 unsigned short row, u64 total,
Namhyung Kim59c624e2016-01-28 00:40:56 +0900891 u64 parent_total,
Namhyung Kim8c430a32015-11-09 14:45:44 +0900892 print_callchain_entry_fn print,
893 struct callchain_print_arg *arg,
894 check_output_full_fn is_output_full)
895{
896 struct rb_node *node;
897 int first_row = row, offset = LEVEL_OFFSET_STEP;
898 bool need_percent;
899
900 node = rb_first(root);
Namhyung Kim59c624e2016-01-28 00:40:56 +0900901 need_percent = check_percent_display(node, parent_total);
Namhyung Kim8c430a32015-11-09 14:45:44 +0900902
903 while (node) {
904 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
905 struct rb_node *next = rb_next(node);
906 struct callchain_list *chain, *first_chain = NULL;
907 int first = true;
908 char *value_str = NULL, *value_str_alloc = NULL;
909 char *chain_str = NULL, *chain_str_alloc = NULL;
910
911 if (arg->row_offset != 0) {
912 arg->row_offset--;
913 goto next;
914 }
915
916 if (need_percent) {
917 char buf[64];
918
919 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
920 if (asprintf(&value_str, "%s", buf) < 0) {
921 value_str = (char *)"<...>";
922 goto do_print;
923 }
924 value_str_alloc = value_str;
925 }
926
927 list_for_each_entry(chain, &child->parent_val, list) {
928 chain_str = hist_browser__folded_callchain_str(browser,
929 chain, value_str, chain_str);
930 if (first) {
931 first = false;
932 first_chain = chain;
933 }
934
935 if (chain_str == NULL) {
936 chain_str = (char *)"Not enough memory!";
937 goto do_print;
938 }
939
940 chain_str_alloc = chain_str;
941 }
942
943 list_for_each_entry(chain, &child->val, list) {
944 chain_str = hist_browser__folded_callchain_str(browser,
945 chain, value_str, chain_str);
946 if (first) {
947 first = false;
948 first_chain = chain;
949 }
950
951 if (chain_str == NULL) {
952 chain_str = (char *)"Not enough memory!";
953 goto do_print;
954 }
955
956 chain_str_alloc = chain_str;
957 }
958
959do_print:
960 print(browser, first_chain, chain_str, offset, row++, arg);
961 free(value_str_alloc);
962 free(chain_str_alloc);
963
964next:
965 if (is_output_full(browser, row))
966 break;
967 node = next;
968 }
969
970 return row - first_row;
971}
972
Namhyung Kim0c841c62016-01-28 00:40:54 +0900973static int hist_browser__show_callchain_graph(struct hist_browser *browser,
Namhyung Kimc09a7e72014-08-21 10:15:45 +0900974 struct rb_root *root, int level,
Namhyung Kim39ee5332014-08-22 09:13:21 +0900975 unsigned short row, u64 total,
Namhyung Kim5eca1042016-01-28 00:40:55 +0900976 u64 parent_total,
Namhyung Kim39ee5332014-08-22 09:13:21 +0900977 print_callchain_entry_fn print,
978 struct callchain_print_arg *arg,
979 check_output_full_fn is_output_full)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300980{
981 struct rb_node *node;
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900982 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
Namhyung Kim4087d112014-11-24 17:13:26 +0900983 bool need_percent;
Namhyung Kim5eca1042016-01-28 00:40:55 +0900984 u64 percent_total = total;
985
986 if (callchain_param.mode == CHAIN_GRAPH_REL)
987 percent_total = parent_total;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300988
Namhyung Kimc09a7e72014-08-21 10:15:45 +0900989 node = rb_first(root);
Namhyung Kim59c624e2016-01-28 00:40:56 +0900990 need_percent = check_percent_display(node, parent_total);
Namhyung Kim4087d112014-11-24 17:13:26 +0900991
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300992 while (node) {
993 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
994 struct rb_node *next = rb_next(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300995 struct callchain_list *chain;
996 char folded_sign = ' ';
997 int first = true;
998 int extra_offset = 0;
999
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001000 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001001 bool was_first = first;
1002
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -03001003 if (first)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001004 first = false;
Namhyung Kim4087d112014-11-24 17:13:26 +09001005 else if (need_percent)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001006 extra_offset = LEVEL_OFFSET_STEP;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001007
1008 folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001009
Namhyung Kim18bb8382015-11-09 14:45:42 +09001010 row += hist_browser__show_callchain_list(browser, child,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001011 chain, row, percent_total,
Namhyung Kim18bb8382015-11-09 14:45:42 +09001012 was_first && need_percent,
1013 offset + extra_offset,
1014 print, arg);
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001015
Namhyung Kim18bb8382015-11-09 14:45:42 +09001016 if (is_output_full(browser, row))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001017 goto out;
Namhyung Kim18bb8382015-11-09 14:45:42 +09001018
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001019 if (folded_sign == '+')
1020 break;
1021 }
1022
1023 if (folded_sign == '-') {
1024 const int new_level = level + (extra_offset ? 2 : 1);
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001025
Namhyung Kim0c841c62016-01-28 00:40:54 +09001026 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001027 new_level, row, total,
1028 child->children_hit,
Namhyung Kim39ee5332014-08-22 09:13:21 +09001029 print, arg, is_output_full);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001030 }
Namhyung Kim39ee5332014-08-22 09:13:21 +09001031 if (is_output_full(browser, row))
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001032 break;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001033 node = next;
1034 }
1035out:
1036 return row - first_row;
1037}
1038
Namhyung Kim0c841c62016-01-28 00:40:54 +09001039static int hist_browser__show_callchain(struct hist_browser *browser,
1040 struct hist_entry *entry, int level,
1041 unsigned short row,
1042 print_callchain_entry_fn print,
1043 struct callchain_print_arg *arg,
1044 check_output_full_fn is_output_full)
1045{
1046 u64 total = hists__total_period(entry->hists);
Namhyung Kim5eca1042016-01-28 00:40:55 +09001047 u64 parent_total;
Namhyung Kim0c841c62016-01-28 00:40:54 +09001048 int printed;
1049
Namhyung Kim5eca1042016-01-28 00:40:55 +09001050 if (symbol_conf.cumulate_callchain)
1051 parent_total = entry->stat_acc->period;
1052 else
1053 parent_total = entry->stat.period;
Namhyung Kim0c841c62016-01-28 00:40:54 +09001054
1055 if (callchain_param.mode == CHAIN_FLAT) {
1056 printed = hist_browser__show_callchain_flat(browser,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001057 &entry->sorted_chain, row,
1058 total, parent_total, print, arg,
1059 is_output_full);
Namhyung Kim0c841c62016-01-28 00:40:54 +09001060 } else if (callchain_param.mode == CHAIN_FOLDED) {
1061 printed = hist_browser__show_callchain_folded(browser,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001062 &entry->sorted_chain, row,
1063 total, parent_total, print, arg,
1064 is_output_full);
Namhyung Kim0c841c62016-01-28 00:40:54 +09001065 } else {
1066 printed = hist_browser__show_callchain_graph(browser,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001067 &entry->sorted_chain, level, row,
1068 total, parent_total, print, arg,
1069 is_output_full);
Namhyung Kim0c841c62016-01-28 00:40:54 +09001070 }
1071
1072 if (arg->is_current_entry)
1073 browser->he_selection = entry;
1074
1075 return printed;
1076}
1077
Namhyung Kim89701462013-01-22 18:09:38 +09001078struct hpp_arg {
1079 struct ui_browser *b;
1080 char folded_sign;
1081 bool current_entry;
1082};
1083
Jiri Olsa98ba1602016-09-22 17:36:35 +02001084int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001085{
1086 struct hpp_arg *arg = hpp->ptr;
Namhyung Kimd6751072014-07-31 14:47:36 +09001087 int ret, len;
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001088 va_list args;
1089 double percent;
1090
1091 va_start(args, fmt);
Namhyung Kimd6751072014-07-31 14:47:36 +09001092 len = va_arg(args, int);
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001093 percent = va_arg(args, double);
1094 va_end(args);
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001095
Namhyung Kim89701462013-01-22 18:09:38 +09001096 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001097
Namhyung Kimd6751072014-07-31 14:47:36 +09001098 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001099 ui_browser__printf(arg->b, "%s", hpp->buf);
Namhyung Kim89701462013-01-22 18:09:38 +09001100
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001101 return ret;
Namhyung Kimf5951d52012-09-03 11:53:09 +09001102}
1103
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001104#define __HPP_COLOR_PERCENT_FN(_type, _field) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001105static u64 __hpp_get_##_field(struct hist_entry *he) \
1106{ \
1107 return he->stat._field; \
1108} \
1109 \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +01001110static int \
Namhyung Kim5b591662014-07-31 14:47:38 +09001111hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +01001112 struct perf_hpp *hpp, \
1113 struct hist_entry *he) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001114{ \
Namhyung Kim5b591662014-07-31 14:47:38 +09001115 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1116 __hpp__slsmg_color_printf, true); \
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001117}
Namhyung Kimf5951d52012-09-03 11:53:09 +09001118
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001119#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1120static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1121{ \
1122 return he->stat_acc->_field; \
1123} \
1124 \
1125static int \
Namhyung Kim5b591662014-07-31 14:47:38 +09001126hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001127 struct perf_hpp *hpp, \
1128 struct hist_entry *he) \
1129{ \
1130 if (!symbol_conf.cumulate_callchain) { \
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001131 struct hpp_arg *arg = hpp->ptr; \
Namhyung Kim5b591662014-07-31 14:47:38 +09001132 int len = fmt->user_len ?: fmt->len; \
Namhyung Kimd6751072014-07-31 14:47:36 +09001133 int ret = scnprintf(hpp->buf, hpp->size, \
Namhyung Kim5b591662014-07-31 14:47:38 +09001134 "%*s", len, "N/A"); \
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001135 ui_browser__printf(arg->b, "%s", hpp->buf); \
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001136 \
1137 return ret; \
1138 } \
Namhyung Kim5b591662014-07-31 14:47:38 +09001139 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1140 " %*.2f%%", __hpp__slsmg_color_printf, true); \
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001141}
1142
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001143__HPP_COLOR_PERCENT_FN(overhead, period)
1144__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1145__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1146__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1147__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001148__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001149
1150#undef __HPP_COLOR_PERCENT_FN
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001151#undef __HPP_COLOR_ACC_PERCENT_FN
Namhyung Kimf5951d52012-09-03 11:53:09 +09001152
1153void hist_browser__init_hpp(void)
1154{
Namhyung Kimf5951d52012-09-03 11:53:09 +09001155 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1156 hist_browser__hpp_color_overhead;
1157 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1158 hist_browser__hpp_color_overhead_sys;
1159 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1160 hist_browser__hpp_color_overhead_us;
1161 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1162 hist_browser__hpp_color_overhead_guest_sys;
1163 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1164 hist_browser__hpp_color_overhead_guest_us;
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001165 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1166 hist_browser__hpp_color_overhead_acc;
Namhyung Kimf5951d52012-09-03 11:53:09 +09001167}
1168
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001169static int hist_browser__show_entry(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001170 struct hist_entry *entry,
1171 unsigned short row)
1172{
Jiri Olsa12400052012-10-13 00:06:16 +02001173 int printed = 0;
Namhyung Kim67d25912012-09-12 15:35:06 +09001174 int width = browser->b.width;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001175 char folded_sign = ' ';
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001176 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001177 off_t row_offset = entry->row_offset;
Namhyung Kim63a1a3d2012-10-15 18:14:35 -03001178 bool first = true;
Jiri Olsa12400052012-10-13 00:06:16 +02001179 struct perf_hpp_fmt *fmt;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001180
1181 if (current_entry) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001182 browser->he_selection = entry;
1183 browser->selection = &entry->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001184 }
1185
1186 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -03001187 hist_entry__init_have_children(entry);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001188 folded_sign = hist_entry__folded(entry);
1189 }
1190
1191 if (row_offset == 0) {
Namhyung Kim89701462013-01-22 18:09:38 +09001192 struct hpp_arg arg = {
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001193 .b = &browser->b,
Namhyung Kim89701462013-01-22 18:09:38 +09001194 .folded_sign = folded_sign,
1195 .current_entry = current_entry,
1196 };
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001197 int column = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001198
Arnaldo Carvalho de Meloca3ff332014-07-01 16:42:24 -03001199 hist_browser__gotorc(browser, row, 0);
Namhyung Kimf5951d52012-09-03 11:53:09 +09001200
Jiri Olsaf0786af2016-01-18 10:24:23 +01001201 hists__for_each_format(browser->hists, fmt) {
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001202 char s[2048];
1203 struct perf_hpp hpp = {
1204 .buf = s,
1205 .size = sizeof(s),
1206 .ptr = &arg,
1207 };
1208
Namhyung Kim361459f2015-12-23 02:07:08 +09001209 if (perf_hpp__should_skip(fmt, entry->hists) ||
1210 column++ < browser->b.horiz_scroll)
Namhyung Kime67d49a2014-03-18 13:00:59 +09001211 continue;
1212
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001213 if (current_entry && browser->b.navkeypressed) {
1214 ui_browser__set_color(&browser->b,
1215 HE_COLORSET_SELECTED);
1216 } else {
1217 ui_browser__set_color(&browser->b,
1218 HE_COLORSET_NORMAL);
1219 }
1220
1221 if (first) {
1222 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001223 ui_browser__printf(&browser->b, "%c ", folded_sign);
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001224 width -= 2;
1225 }
1226 first = false;
1227 } else {
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001228 ui_browser__printf(&browser->b, " ");
Namhyung Kimf5951d52012-09-03 11:53:09 +09001229 width -= 2;
1230 }
1231
Jiri Olsa12400052012-10-13 00:06:16 +02001232 if (fmt->color) {
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001233 int ret = fmt->color(fmt, &hpp, entry);
1234 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1235 /*
1236 * fmt->color() already used ui_browser to
1237 * print the non alignment bits, skip it (+ret):
1238 */
1239 ui_browser__printf(&browser->b, "%s", s + ret);
Namhyung Kimf5951d52012-09-03 11:53:09 +09001240 } else {
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001241 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001242 ui_browser__printf(&browser->b, "%s", s);
Namhyung Kimf5951d52012-09-03 11:53:09 +09001243 }
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001244 width -= hpp.buf - s;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001245 }
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -02001246
1247 /* The scroll bar isn't being used */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001248 if (!browser->b.navkeypressed)
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -02001249 width += 1;
1250
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -03001251 ui_browser__write_nstring(&browser->b, "", width);
Namhyung Kim26d8b332014-03-03 16:16:20 +09001252
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001253 ++row;
1254 ++printed;
1255 } else
1256 --row_offset;
1257
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -03001258 if (folded_sign == '-' && row != browser->b.rows) {
Namhyung Kim39ee5332014-08-22 09:13:21 +09001259 struct callchain_print_arg arg = {
1260 .row_offset = row_offset,
1261 .is_current_entry = current_entry,
1262 };
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001263
Namhyung Kim0c841c62016-01-28 00:40:54 +09001264 printed += hist_browser__show_callchain(browser, entry, 1, row,
Namhyung Kim4b3a3212015-11-09 14:45:43 +09001265 hist_browser__show_callchain_entry, &arg,
1266 hist_browser__check_output_full);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001267 }
1268
1269 return printed;
1270}
1271
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001272static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1273 struct hist_entry *entry,
1274 unsigned short row,
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001275 int level)
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001276{
1277 int printed = 0;
1278 int width = browser->b.width;
1279 char folded_sign = ' ';
1280 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1281 off_t row_offset = entry->row_offset;
1282 bool first = true;
1283 struct perf_hpp_fmt *fmt;
Namhyung Kima61a22f2016-03-07 16:44:50 -03001284 struct perf_hpp_list_node *fmt_node;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001285 struct hpp_arg arg = {
1286 .b = &browser->b,
1287 .current_entry = current_entry,
1288 };
1289 int column = 0;
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001290 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001291
1292 if (current_entry) {
1293 browser->he_selection = entry;
1294 browser->selection = &entry->ms;
1295 }
1296
1297 hist_entry__init_have_children(entry);
1298 folded_sign = hist_entry__folded(entry);
1299 arg.folded_sign = folded_sign;
1300
1301 if (entry->leaf && row_offset) {
1302 row_offset--;
1303 goto show_callchain;
1304 }
1305
1306 hist_browser__gotorc(browser, row, 0);
1307
1308 if (current_entry && browser->b.navkeypressed)
1309 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1310 else
1311 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1312
1313 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1314 width -= level * HIERARCHY_INDENT;
1315
Namhyung Kima61a22f2016-03-07 16:44:50 -03001316 /* the first hpp_list_node is for overhead columns */
1317 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1318 struct perf_hpp_list_node, list);
1319 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001320 char s[2048];
1321 struct perf_hpp hpp = {
1322 .buf = s,
1323 .size = sizeof(s),
1324 .ptr = &arg,
1325 };
1326
1327 if (perf_hpp__should_skip(fmt, entry->hists) ||
1328 column++ < browser->b.horiz_scroll)
1329 continue;
1330
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001331 if (current_entry && browser->b.navkeypressed) {
1332 ui_browser__set_color(&browser->b,
1333 HE_COLORSET_SELECTED);
1334 } else {
1335 ui_browser__set_color(&browser->b,
1336 HE_COLORSET_NORMAL);
1337 }
1338
1339 if (first) {
Namhyung Kim3d9f4682016-11-08 22:08:30 +09001340 ui_browser__printf(&browser->b, "%c ", folded_sign);
1341 width -= 2;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001342 first = false;
1343 } else {
1344 ui_browser__printf(&browser->b, " ");
1345 width -= 2;
1346 }
1347
1348 if (fmt->color) {
1349 int ret = fmt->color(fmt, &hpp, entry);
1350 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1351 /*
1352 * fmt->color() already used ui_browser to
1353 * print the non alignment bits, skip it (+ret):
1354 */
1355 ui_browser__printf(&browser->b, "%s", s + ret);
1356 } else {
1357 int ret = fmt->entry(fmt, &hpp, entry);
1358 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1359 ui_browser__printf(&browser->b, "%s", s);
1360 }
1361 width -= hpp.buf - s;
1362 }
1363
Namhyung Kimb9bf9112016-11-08 22:08:32 +09001364 if (!first) {
1365 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1366 width -= hierarchy_indent;
1367 }
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001368
1369 if (column >= browser->b.horiz_scroll) {
1370 char s[2048];
1371 struct perf_hpp hpp = {
1372 .buf = s,
1373 .size = sizeof(s),
1374 .ptr = &arg,
1375 };
1376
1377 if (current_entry && browser->b.navkeypressed) {
1378 ui_browser__set_color(&browser->b,
1379 HE_COLORSET_SELECTED);
1380 } else {
1381 ui_browser__set_color(&browser->b,
1382 HE_COLORSET_NORMAL);
1383 }
1384
Namhyung Kim1b2dbbf2016-03-07 16:44:46 -03001385 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
Namhyung Kim131d51e2016-11-08 22:08:31 +09001386 if (first) {
1387 ui_browser__printf(&browser->b, "%c ", folded_sign);
1388 first = false;
1389 } else {
1390 ui_browser__write_nstring(&browser->b, "", 2);
1391 }
1392
Namhyung Kim1b2dbbf2016-03-07 16:44:46 -03001393 width -= 2;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001394
Namhyung Kim1b2dbbf2016-03-07 16:44:46 -03001395 /*
1396 * No need to call hist_entry__snprintf_alignment()
1397 * since this fmt is always the last column in the
1398 * hierarchy mode.
1399 */
1400 if (fmt->color) {
1401 width -= fmt->color(fmt, &hpp, entry);
1402 } else {
1403 int i = 0;
Namhyung Kimcb1fab92016-02-27 03:52:45 +09001404
Namhyung Kim1b2dbbf2016-03-07 16:44:46 -03001405 width -= fmt->entry(fmt, &hpp, entry);
1406 ui_browser__printf(&browser->b, "%s", ltrim(s));
Namhyung Kimcb1fab92016-02-27 03:52:45 +09001407
Namhyung Kim1b2dbbf2016-03-07 16:44:46 -03001408 while (isspace(s[i++]))
1409 width++;
1410 }
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001411 }
1412 }
1413
1414 /* The scroll bar isn't being used */
1415 if (!browser->b.navkeypressed)
1416 width += 1;
1417
1418 ui_browser__write_nstring(&browser->b, "", width);
1419
1420 ++row;
1421 ++printed;
1422
1423show_callchain:
1424 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1425 struct callchain_print_arg carg = {
1426 .row_offset = row_offset,
1427 };
1428
1429 printed += hist_browser__show_callchain(browser, entry,
1430 level + 1, row,
1431 hist_browser__show_callchain_entry, &carg,
1432 hist_browser__check_output_full);
1433 }
1434
1435 return printed;
1436}
1437
Namhyung Kim79dded82016-02-26 21:13:19 +09001438static int hist_browser__show_no_entry(struct hist_browser *browser,
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001439 unsigned short row, int level)
Namhyung Kim79dded82016-02-26 21:13:19 +09001440{
1441 int width = browser->b.width;
1442 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1443 bool first = true;
1444 int column = 0;
1445 int ret;
1446 struct perf_hpp_fmt *fmt;
Namhyung Kima61a22f2016-03-07 16:44:50 -03001447 struct perf_hpp_list_node *fmt_node;
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001448 int indent = browser->hists->nr_hpp_node - 2;
Namhyung Kim79dded82016-02-26 21:13:19 +09001449
1450 if (current_entry) {
1451 browser->he_selection = NULL;
1452 browser->selection = NULL;
1453 }
1454
1455 hist_browser__gotorc(browser, row, 0);
1456
1457 if (current_entry && browser->b.navkeypressed)
1458 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1459 else
1460 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1461
1462 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1463 width -= level * HIERARCHY_INDENT;
1464
Namhyung Kima61a22f2016-03-07 16:44:50 -03001465 /* the first hpp_list_node is for overhead columns */
1466 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1467 struct perf_hpp_list_node, list);
1468 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
Namhyung Kim79dded82016-02-26 21:13:19 +09001469 if (perf_hpp__should_skip(fmt, browser->hists) ||
1470 column++ < browser->b.horiz_scroll)
1471 continue;
1472
Jiri Olsada1b0402016-06-14 20:19:20 +02001473 ret = fmt->width(fmt, NULL, browser->hists);
Namhyung Kim79dded82016-02-26 21:13:19 +09001474
1475 if (first) {
1476 /* for folded sign */
1477 first = false;
1478 ret++;
1479 } else {
1480 /* space between columns */
1481 ret += 2;
1482 }
1483
1484 ui_browser__write_nstring(&browser->b, "", ret);
1485 width -= ret;
1486 }
1487
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001488 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1489 width -= indent * HIERARCHY_INDENT;
Namhyung Kim79dded82016-02-26 21:13:19 +09001490
1491 if (column >= browser->b.horiz_scroll) {
1492 char buf[32];
1493
1494 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1495 ui_browser__printf(&browser->b, " %s", buf);
1496 width -= ret + 2;
1497 }
1498
1499 /* The scroll bar isn't being used */
1500 if (!browser->b.navkeypressed)
1501 width += 1;
1502
1503 ui_browser__write_nstring(&browser->b, "", width);
1504 return 1;
1505}
1506
Jiri Olsa81a888f2014-06-14 15:44:52 +02001507static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1508{
1509 advance_hpp(hpp, inc);
1510 return hpp->size <= 0;
1511}
1512
Jiri Olsa69705b32016-08-07 17:28:28 +02001513static int
1514hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1515 size_t size, int line)
Jiri Olsa81a888f2014-06-14 15:44:52 +02001516{
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001517 struct hists *hists = browser->hists;
Jiri Olsa81a888f2014-06-14 15:44:52 +02001518 struct perf_hpp dummy_hpp = {
1519 .buf = buf,
1520 .size = size,
1521 };
1522 struct perf_hpp_fmt *fmt;
1523 size_t ret = 0;
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001524 int column = 0;
Jiri Olsa29659ab2016-08-07 17:28:30 +02001525 int span = 0;
Jiri Olsa81a888f2014-06-14 15:44:52 +02001526
1527 if (symbol_conf.use_callchain) {
1528 ret = scnprintf(buf, size, " ");
1529 if (advance_hpp_check(&dummy_hpp, ret))
1530 return ret;
1531 }
1532
Jiri Olsaf0786af2016-01-18 10:24:23 +01001533 hists__for_each_format(browser->hists, fmt) {
Namhyung Kim361459f2015-12-23 02:07:08 +09001534 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
Jiri Olsa81a888f2014-06-14 15:44:52 +02001535 continue;
1536
Jiri Olsa29659ab2016-08-07 17:28:30 +02001537 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
Jiri Olsa81a888f2014-06-14 15:44:52 +02001538 if (advance_hpp_check(&dummy_hpp, ret))
1539 break;
1540
Jiri Olsa29659ab2016-08-07 17:28:30 +02001541 if (span)
1542 continue;
1543
Jiri Olsa81a888f2014-06-14 15:44:52 +02001544 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1545 if (advance_hpp_check(&dummy_hpp, ret))
1546 break;
1547 }
1548
1549 return ret;
1550}
1551
Namhyung Kimd8b92402016-02-25 00:13:46 +09001552static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1553{
1554 struct hists *hists = browser->hists;
1555 struct perf_hpp dummy_hpp = {
1556 .buf = buf,
1557 .size = size,
1558 };
1559 struct perf_hpp_fmt *fmt;
Namhyung Kima61a22f2016-03-07 16:44:50 -03001560 struct perf_hpp_list_node *fmt_node;
Namhyung Kimd8b92402016-02-25 00:13:46 +09001561 size_t ret = 0;
1562 int column = 0;
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001563 int indent = hists->nr_hpp_node - 2;
Namhyung Kima61a22f2016-03-07 16:44:50 -03001564 bool first_node, first_col;
Namhyung Kimd8b92402016-02-25 00:13:46 +09001565
Namhyung Kim3d9f4682016-11-08 22:08:30 +09001566 ret = scnprintf(buf, size, " ");
Namhyung Kimd8b92402016-02-25 00:13:46 +09001567 if (advance_hpp_check(&dummy_hpp, ret))
1568 return ret;
1569
Namhyung Kimb9bf9112016-11-08 22:08:32 +09001570 first_node = true;
Namhyung Kima61a22f2016-03-07 16:44:50 -03001571 /* the first hpp_list_node is for overhead columns */
1572 fmt_node = list_first_entry(&hists->hpp_formats,
1573 struct perf_hpp_list_node, list);
1574 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
Namhyung Kimd8b92402016-02-25 00:13:46 +09001575 if (column++ < browser->b.horiz_scroll)
1576 continue;
1577
Jiri Olsa29659ab2016-08-07 17:28:30 +02001578 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
Namhyung Kimd8b92402016-02-25 00:13:46 +09001579 if (advance_hpp_check(&dummy_hpp, ret))
1580 break;
1581
1582 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1583 if (advance_hpp_check(&dummy_hpp, ret))
1584 break;
Namhyung Kimb9bf9112016-11-08 22:08:32 +09001585
1586 first_node = false;
Namhyung Kimd8b92402016-02-25 00:13:46 +09001587 }
1588
Namhyung Kimb9bf9112016-11-08 22:08:32 +09001589 if (!first_node) {
1590 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1591 indent * HIERARCHY_INDENT, "");
1592 if (advance_hpp_check(&dummy_hpp, ret))
1593 return ret;
1594 }
Namhyung Kimd8b92402016-02-25 00:13:46 +09001595
Namhyung Kima61a22f2016-03-07 16:44:50 -03001596 first_node = true;
1597 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1598 if (!first_node) {
Namhyung Kimd8b92402016-02-25 00:13:46 +09001599 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1600 if (advance_hpp_check(&dummy_hpp, ret))
1601 break;
1602 }
Namhyung Kima61a22f2016-03-07 16:44:50 -03001603 first_node = false;
Namhyung Kimd8b92402016-02-25 00:13:46 +09001604
Namhyung Kima61a22f2016-03-07 16:44:50 -03001605 first_col = true;
1606 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1607 char *start;
Namhyung Kimd8b92402016-02-25 00:13:46 +09001608
Namhyung Kima61a22f2016-03-07 16:44:50 -03001609 if (perf_hpp__should_skip(fmt, hists))
1610 continue;
Namhyung Kimcb1fab92016-02-27 03:52:45 +09001611
Namhyung Kima61a22f2016-03-07 16:44:50 -03001612 if (!first_col) {
1613 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1614 if (advance_hpp_check(&dummy_hpp, ret))
1615 break;
1616 }
1617 first_col = false;
Namhyung Kimcb1fab92016-02-27 03:52:45 +09001618
Jiri Olsa29659ab2016-08-07 17:28:30 +02001619 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
Namhyung Kima61a22f2016-03-07 16:44:50 -03001620 dummy_hpp.buf[ret] = '\0';
Namhyung Kima61a22f2016-03-07 16:44:50 -03001621
Jiri Olsa7d6a7e72016-04-07 09:11:11 +02001622 start = trim(dummy_hpp.buf);
Namhyung Kima61a22f2016-03-07 16:44:50 -03001623 ret = strlen(start);
1624
1625 if (start != dummy_hpp.buf)
1626 memmove(dummy_hpp.buf, start, ret + 1);
1627
1628 if (advance_hpp_check(&dummy_hpp, ret))
1629 break;
1630 }
Namhyung Kimd8b92402016-02-25 00:13:46 +09001631 }
1632
1633 return ret;
1634}
1635
Jiri Olsa01b47702016-06-14 20:19:13 +02001636static void hists_browser__hierarchy_headers(struct hist_browser *browser)
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001637{
Jiri Olsa81a888f2014-06-14 15:44:52 +02001638 char headers[1024];
1639
Jiri Olsa01b47702016-06-14 20:19:13 +02001640 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1641 sizeof(headers));
1642
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001643 ui_browser__gotorc(&browser->b, 0, 0);
1644 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -03001645 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001646}
1647
Jiri Olsa01b47702016-06-14 20:19:13 +02001648static void hists_browser__headers(struct hist_browser *browser)
1649{
Jiri Olsa69705b32016-08-07 17:28:28 +02001650 struct hists *hists = browser->hists;
1651 struct perf_hpp_list *hpp_list = hists->hpp_list;
Jiri Olsa01b47702016-06-14 20:19:13 +02001652
Jiri Olsa69705b32016-08-07 17:28:28 +02001653 int line;
Jiri Olsa01b47702016-06-14 20:19:13 +02001654
Jiri Olsa69705b32016-08-07 17:28:28 +02001655 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1656 char headers[1024];
1657
1658 hists_browser__scnprintf_headers(browser, headers,
1659 sizeof(headers), line);
1660
1661 ui_browser__gotorc(&browser->b, line, 0);
1662 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1663 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1664 }
Jiri Olsa01b47702016-06-14 20:19:13 +02001665}
1666
1667static void hist_browser__show_headers(struct hist_browser *browser)
1668{
1669 if (symbol_conf.report_hierarchy)
1670 hists_browser__hierarchy_headers(browser);
1671 else
1672 hists_browser__headers(browser);
1673}
1674
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -03001675static void ui_browser__hists_init_top(struct ui_browser *browser)
1676{
1677 if (browser->top == NULL) {
1678 struct hist_browser *hb;
1679
1680 hb = container_of(browser, struct hist_browser, b);
1681 browser->top = rb_first(&hb->hists->entries);
1682 }
1683}
1684
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001685static unsigned int hist_browser__refresh(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001686{
1687 unsigned row = 0;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001688 u16 header_offset = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001689 struct rb_node *nd;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001690 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
Jiri Olsaf8e67102016-08-07 17:28:26 +02001691 struct hists *hists = hb->hists;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001692
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001693 if (hb->show_headers) {
Jiri Olsaf8e67102016-08-07 17:28:26 +02001694 struct perf_hpp_list *hpp_list = hists->hpp_list;
1695
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001696 hist_browser__show_headers(hb);
Jiri Olsaf8e67102016-08-07 17:28:26 +02001697 header_offset = hpp_list->nr_header_lines;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001698 }
1699
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001700 ui_browser__hists_init_top(browser);
Wang Nan979d2ca2015-12-07 02:35:46 +00001701 hb->he_selection = NULL;
1702 hb->selection = NULL;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001703
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001704 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001705 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +09001706 float percent;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001707
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001708 if (h->filtered) {
1709 /* let it move to sibling */
1710 h->unfolded = false;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001711 continue;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001712 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001713
Namhyung Kim14135662013-10-31 10:17:39 +09001714 percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +09001715 if (percent < hb->min_pcnt)
1716 continue;
1717
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001718 if (symbol_conf.report_hierarchy) {
1719 row += hist_browser__show_hierarchy_entry(hb, h, row,
Namhyung Kim2dbbe9f2016-03-07 16:44:48 -03001720 h->depth);
Namhyung Kim79dded82016-02-26 21:13:19 +09001721 if (row == browser->rows)
1722 break;
1723
1724 if (h->has_no_entry) {
Namhyung Kima61a22f2016-03-07 16:44:50 -03001725 hist_browser__show_no_entry(hb, row, h->depth + 1);
Namhyung Kim79dded82016-02-26 21:13:19 +09001726 row++;
1727 }
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001728 } else {
1729 row += hist_browser__show_entry(hb, h, row);
1730 }
1731
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -03001732 if (row == browser->rows)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001733 break;
1734 }
1735
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001736 return row + header_offset;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001737}
1738
Namhyung Kim064f1982013-05-14 11:09:04 +09001739static struct rb_node *hists__filter_entries(struct rb_node *nd,
Namhyung Kim064f1982013-05-14 11:09:04 +09001740 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001741{
1742 while (nd != NULL) {
1743 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +09001744 float percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +09001745
Namhyung Kimc0f15272014-04-16 11:16:33 +09001746 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001747 return nd;
1748
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001749 /*
1750 * If it's filtered, its all children also were filtered.
1751 * So move to sibling node.
1752 */
1753 if (rb_next(nd))
1754 nd = rb_next(nd);
1755 else
1756 nd = rb_hierarchy_next(nd);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001757 }
1758
1759 return NULL;
1760}
1761
Namhyung Kim064f1982013-05-14 11:09:04 +09001762static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
Namhyung Kim064f1982013-05-14 11:09:04 +09001763 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001764{
1765 while (nd != NULL) {
1766 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +09001767 float percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +09001768
1769 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001770 return nd;
1771
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001772 nd = rb_hierarchy_prev(nd);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001773 }
1774
1775 return NULL;
1776}
1777
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001778static void ui_browser__hists_seek(struct ui_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001779 off_t offset, int whence)
1780{
1781 struct hist_entry *h;
1782 struct rb_node *nd;
1783 bool first = true;
Namhyung Kim064f1982013-05-14 11:09:04 +09001784 struct hist_browser *hb;
1785
1786 hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001787
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001788 if (browser->nr_entries == 0)
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001789 return;
1790
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001791 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -03001792
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001793 switch (whence) {
1794 case SEEK_SET:
Namhyung Kim064f1982013-05-14 11:09:04 +09001795 nd = hists__filter_entries(rb_first(browser->entries),
Namhyung Kim14135662013-10-31 10:17:39 +09001796 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001797 break;
1798 case SEEK_CUR:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001799 nd = browser->top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001800 goto do_offset;
1801 case SEEK_END:
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001802 nd = rb_hierarchy_last(rb_last(browser->entries));
1803 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001804 first = false;
1805 break;
1806 default:
1807 return;
1808 }
1809
1810 /*
1811 * Moves not relative to the first visible entry invalidates its
1812 * row_offset:
1813 */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001814 h = rb_entry(browser->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001815 h->row_offset = 0;
1816
1817 /*
1818 * Here we have to check if nd is expanded (+), if it is we can't go
1819 * the next top level hist_entry, instead we must compute an offset of
1820 * what _not_ to show and not change the first visible entry.
1821 *
1822 * This offset increments when we are going from top to bottom and
1823 * decreases when we're going from bottom to top.
1824 *
1825 * As we don't have backpointers to the top level in the callchains
1826 * structure, we need to always print the whole hist_entry callchain,
1827 * skipping the first ones that are before the first visible entry
1828 * and stop when we printed enough lines to fill the screen.
1829 */
1830do_offset:
Wang Nan837eeb72015-12-07 02:35:45 +00001831 if (!nd)
1832 return;
1833
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001834 if (offset > 0) {
1835 do {
1836 h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001837 if (h->unfolded && h->leaf) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001838 u16 remaining = h->nr_rows - h->row_offset;
1839 if (offset > remaining) {
1840 offset -= remaining;
1841 h->row_offset = 0;
1842 } else {
1843 h->row_offset += offset;
1844 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001845 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001846 break;
1847 }
1848 }
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001849 nd = hists__filter_entries(rb_hierarchy_next(nd),
1850 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001851 if (nd == NULL)
1852 break;
1853 --offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001854 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001855 } while (offset != 0);
1856 } else if (offset < 0) {
1857 while (1) {
1858 h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001859 if (h->unfolded && h->leaf) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001860 if (first) {
1861 if (-offset > h->row_offset) {
1862 offset += h->row_offset;
1863 h->row_offset = 0;
1864 } else {
1865 h->row_offset += offset;
1866 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001867 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001868 break;
1869 }
1870 } else {
1871 if (-offset > h->nr_rows) {
1872 offset += h->nr_rows;
1873 h->row_offset = 0;
1874 } else {
1875 h->row_offset = h->nr_rows + offset;
1876 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001877 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001878 break;
1879 }
1880 }
1881 }
1882
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001883 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
Namhyung Kim064f1982013-05-14 11:09:04 +09001884 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001885 if (nd == NULL)
1886 break;
1887 ++offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001888 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001889 if (offset == 0) {
1890 /*
1891 * Last unfiltered hist_entry, check if it is
1892 * unfolded, if it is then we should have
1893 * row_offset at its last entry.
1894 */
1895 h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001896 if (h->unfolded && h->leaf)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001897 h->row_offset = h->nr_rows;
1898 break;
1899 }
1900 first = false;
1901 }
1902 } else {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001903 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001904 h = rb_entry(nd, struct hist_entry, rb_node);
1905 h->row_offset = 0;
1906 }
1907}
1908
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001909static int hist_browser__fprintf_callchain(struct hist_browser *browser,
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001910 struct hist_entry *he, FILE *fp,
1911 int level)
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001912{
Namhyung Kim39ee5332014-08-22 09:13:21 +09001913 struct callchain_print_arg arg = {
1914 .fp = fp,
1915 };
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001916
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001917 hist_browser__show_callchain(browser, he, level, 0,
Namhyung Kim39ee5332014-08-22 09:13:21 +09001918 hist_browser__fprintf_callchain_entry, &arg,
1919 hist_browser__check_dump_full);
1920 return arg.printed;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001921}
1922
1923static int hist_browser__fprintf_entry(struct hist_browser *browser,
1924 struct hist_entry *he, FILE *fp)
1925{
1926 char s[8192];
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001927 int printed = 0;
1928 char folded_sign = ' ';
Namhyung Kim26d8b332014-03-03 16:16:20 +09001929 struct perf_hpp hpp = {
1930 .buf = s,
1931 .size = sizeof(s),
1932 };
1933 struct perf_hpp_fmt *fmt;
1934 bool first = true;
1935 int ret;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001936
Arnaldo Carvalho de Melo1b6b6782016-04-18 12:24:41 -03001937 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001938 folded_sign = hist_entry__folded(he);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001939 printed += fprintf(fp, "%c ", folded_sign);
Arnaldo Carvalho de Melo1b6b6782016-04-18 12:24:41 -03001940 }
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001941
Jiri Olsaf0786af2016-01-18 10:24:23 +01001942 hists__for_each_format(browser->hists, fmt) {
Namhyung Kim361459f2015-12-23 02:07:08 +09001943 if (perf_hpp__should_skip(fmt, he->hists))
Namhyung Kime67d49a2014-03-18 13:00:59 +09001944 continue;
1945
Namhyung Kim26d8b332014-03-03 16:16:20 +09001946 if (!first) {
1947 ret = scnprintf(hpp.buf, hpp.size, " ");
1948 advance_hpp(&hpp, ret);
1949 } else
1950 first = false;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001951
Namhyung Kim26d8b332014-03-03 16:16:20 +09001952 ret = fmt->entry(fmt, &hpp, he);
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001953 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
Namhyung Kim26d8b332014-03-03 16:16:20 +09001954 advance_hpp(&hpp, ret);
1955 }
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001956 printed += fprintf(fp, "%s\n", s);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001957
1958 if (folded_sign == '-')
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001959 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1960
1961 return printed;
1962}
1963
1964
1965static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1966 struct hist_entry *he,
Namhyung Kim325a6282016-03-09 22:47:00 +09001967 FILE *fp, int level)
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001968{
1969 char s[8192];
1970 int printed = 0;
1971 char folded_sign = ' ';
1972 struct perf_hpp hpp = {
1973 .buf = s,
1974 .size = sizeof(s),
1975 };
1976 struct perf_hpp_fmt *fmt;
Namhyung Kim325a6282016-03-09 22:47:00 +09001977 struct perf_hpp_list_node *fmt_node;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001978 bool first = true;
1979 int ret;
Namhyung Kim325a6282016-03-09 22:47:00 +09001980 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001981
1982 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1983
1984 folded_sign = hist_entry__folded(he);
1985 printed += fprintf(fp, "%c", folded_sign);
1986
Namhyung Kim325a6282016-03-09 22:47:00 +09001987 /* the first hpp_list_node is for overhead columns */
1988 fmt_node = list_first_entry(&he->hists->hpp_formats,
1989 struct perf_hpp_list_node, list);
1990 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001991 if (!first) {
1992 ret = scnprintf(hpp.buf, hpp.size, " ");
1993 advance_hpp(&hpp, ret);
1994 } else
1995 first = false;
1996
1997 ret = fmt->entry(fmt, &hpp, he);
1998 advance_hpp(&hpp, ret);
1999 }
2000
2001 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2002 advance_hpp(&hpp, ret);
2003
Namhyung Kim1b2dbbf2016-03-07 16:44:46 -03002004 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2005 ret = scnprintf(hpp.buf, hpp.size, " ");
2006 advance_hpp(&hpp, ret);
2007
2008 ret = fmt->entry(fmt, &hpp, he);
2009 advance_hpp(&hpp, ret);
2010 }
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002011
2012 printed += fprintf(fp, "%s\n", rtrim(s));
2013
2014 if (he->leaf && folded_sign == '-') {
2015 printed += hist_browser__fprintf_callchain(browser, he, fp,
2016 he->depth + 1);
2017 }
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03002018
2019 return printed;
2020}
2021
2022static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2023{
Namhyung Kim064f1982013-05-14 11:09:04 +09002024 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
Namhyung Kim064f1982013-05-14 11:09:04 +09002025 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03002026 int printed = 0;
2027
2028 while (nd) {
2029 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2030
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002031 if (symbol_conf.report_hierarchy) {
2032 printed += hist_browser__fprintf_hierarchy_entry(browser,
2033 h, fp,
Namhyung Kim325a6282016-03-09 22:47:00 +09002034 h->depth);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002035 } else {
2036 printed += hist_browser__fprintf_entry(browser, h, fp);
2037 }
2038
2039 nd = hists__filter_entries(rb_hierarchy_next(nd),
2040 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03002041 }
2042
2043 return printed;
2044}
2045
2046static int hist_browser__dump(struct hist_browser *browser)
2047{
2048 char filename[64];
2049 FILE *fp;
2050
2051 while (1) {
2052 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2053 if (access(filename, F_OK))
2054 break;
2055 /*
2056 * XXX: Just an arbitrary lazy upper limit
2057 */
2058 if (++browser->print_seq == 8192) {
2059 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2060 return -1;
2061 }
2062 }
2063
2064 fp = fopen(filename, "w");
2065 if (fp == NULL) {
2066 char bf[64];
Arnaldo Carvalho de Meloc8b5f2c2016-07-06 11:56:20 -03002067 const char *err = str_error_r(errno, bf, sizeof(bf));
Kirill A. Shutemov4cc49d42012-07-24 00:06:54 +03002068 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03002069 return -1;
2070 }
2071
2072 ++browser->print_seq;
2073 hist_browser__fprintf(browser, fp);
2074 fclose(fp);
2075 ui_helpline__fpush("%s written!", filename);
2076
2077 return 0;
2078}
2079
Jiri Olsafcd864262016-06-20 23:58:18 +02002080void hist_browser__init(struct hist_browser *browser,
2081 struct hists *hists)
2082{
2083 struct perf_hpp_fmt *fmt;
2084
2085 browser->hists = hists;
2086 browser->b.refresh = hist_browser__refresh;
2087 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2088 browser->b.seek = ui_browser__hists_seek;
2089 browser->b.use_navkeypressed = true;
2090 browser->show_headers = symbol_conf.show_hist_headers;
2091
Namhyung Kim9cba98442016-10-25 01:21:10 +09002092 if (symbol_conf.report_hierarchy) {
2093 struct perf_hpp_list_node *fmt_node;
2094
2095 /* count overhead columns (in the first node) */
2096 fmt_node = list_first_entry(&hists->hpp_formats,
2097 struct perf_hpp_list_node, list);
2098 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2099 ++browser->b.columns;
2100
2101 /* add a single column for whole hierarchy sort keys*/
Jiri Olsafcd864262016-06-20 23:58:18 +02002102 ++browser->b.columns;
Namhyung Kim9cba98442016-10-25 01:21:10 +09002103 } else {
2104 hists__for_each_format(hists, fmt)
2105 ++browser->b.columns;
2106 }
Namhyung Kime3b60bc2016-09-20 14:30:24 +09002107
2108 hists__reset_column_width(hists);
Jiri Olsafcd864262016-06-20 23:58:18 +02002109}
2110
Jiri Olsaa6ec8942016-06-20 23:58:17 +02002111struct hist_browser *hist_browser__new(struct hists *hists)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002112{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002113 struct hist_browser *browser = zalloc(sizeof(*browser));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002114
Jiri Olsafcd864262016-06-20 23:58:18 +02002115 if (browser)
2116 hist_browser__init(browser, hists);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002117
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002118 return browser;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002119}
2120
Jiri Olsaa6ec8942016-06-20 23:58:17 +02002121static struct hist_browser *
2122perf_evsel_browser__new(struct perf_evsel *evsel,
2123 struct hist_browser_timer *hbt,
2124 struct perf_env *env)
2125{
2126 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2127
2128 if (browser) {
2129 browser->hbt = hbt;
2130 browser->env = env;
2131 browser->title = perf_evsel_browser_title;
2132 }
2133 return browser;
2134}
2135
Jiri Olsadabd2012016-06-20 23:58:14 +02002136void hist_browser__delete(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002137{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002138 free(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002139}
2140
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002141static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002142{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002143 return browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002144}
2145
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002146static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002147{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002148 return browser->he_selection->thread;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002149}
2150
Taeung Song1e378eb2014-10-07 16:13:15 +09002151/* Check whether the browser is for 'top' or 'report' */
2152static inline bool is_report_browser(void *timer)
2153{
2154 return timer == NULL;
2155}
2156
Jiri Olsa5b91a862016-06-20 23:58:15 +02002157static int perf_evsel_browser_title(struct hist_browser *browser,
Taeung Song1e378eb2014-10-07 16:13:15 +09002158 char *bf, size_t size)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002159{
Jiri Olsa5b91a862016-06-20 23:58:15 +02002160 struct hist_browser_timer *hbt = browser->hbt;
2161 struct hists *hists = browser->hists;
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03002162 char unit;
2163 int printed;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002164 const struct dso *dso = hists->dso_filter;
2165 const struct thread *thread = hists->thread_filter;
Kan Liang84734b02015-09-04 10:45:45 -04002166 int socket_id = hists->socket_filter;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002167 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2168 u64 nr_events = hists->stats.total_period;
Namhyung Kim717e2632013-01-22 18:09:44 +09002169 struct perf_evsel *evsel = hists_to_evsel(hists);
Jiri Olsadd00d482014-06-19 13:41:13 +02002170 const char *ev_name = perf_evsel__name(evsel);
Namhyung Kim717e2632013-01-22 18:09:44 +09002171 char buf[512];
2172 size_t buflen = sizeof(buf);
Kan Liang9e207dd2015-08-11 06:30:49 -04002173 char ref[30] = " show reference callgraph, ";
2174 bool enable_ref = false;
Namhyung Kim717e2632013-01-22 18:09:44 +09002175
Namhyung Kimf2148332014-01-14 11:52:48 +09002176 if (symbol_conf.filter_relative) {
2177 nr_samples = hists->stats.nr_non_filtered_samples;
2178 nr_events = hists->stats.total_non_filtered_period;
2179 }
2180
Namhyung Kim759ff492013-03-05 14:53:26 +09002181 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09002182 struct perf_evsel *pos;
2183
2184 perf_evsel__group_desc(evsel, buf, buflen);
2185 ev_name = buf;
2186
2187 for_each_group_member(pos, evsel) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002188 struct hists *pos_hists = evsel__hists(pos);
2189
Namhyung Kimf2148332014-01-14 11:52:48 +09002190 if (symbol_conf.filter_relative) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002191 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2192 nr_events += pos_hists->stats.total_non_filtered_period;
Namhyung Kimf2148332014-01-14 11:52:48 +09002193 } else {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002194 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2195 nr_events += pos_hists->stats.total_period;
Namhyung Kimf2148332014-01-14 11:52:48 +09002196 }
Namhyung Kim717e2632013-01-22 18:09:44 +09002197 }
2198 }
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03002199
Kan Liang9e207dd2015-08-11 06:30:49 -04002200 if (symbol_conf.show_ref_callgraph &&
2201 strstr(ev_name, "call-graph=no"))
2202 enable_ref = true;
Ashay Ranecc686282012-04-05 21:01:01 -05002203 nr_samples = convert_unit(nr_samples, &unit);
2204 printed = scnprintf(bf, size,
Kan Liang9e207dd2015-08-11 06:30:49 -04002205 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2206 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
Ashay Ranecc686282012-04-05 21:01:01 -05002207
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002208
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002209 if (hists->uid_filter_str)
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02002210 printed += snprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002211 ", UID: %s", hists->uid_filter_str);
Namhyung Kim6962ccb2016-03-10 00:14:50 +09002212 if (thread) {
Jiri Olsafa829112016-05-03 13:54:47 +02002213 if (hists__has(hists, thread)) {
Namhyung Kim6962ccb2016-03-10 00:14:50 +09002214 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03002215 ", Thread: %s(%d)",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02002216 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03002217 thread->tid);
Namhyung Kim6962ccb2016-03-10 00:14:50 +09002218 } else {
2219 printed += scnprintf(bf + printed, size - printed,
2220 ", Thread: %s",
2221 (thread->comm_set ? thread__comm_str(thread) : ""));
2222 }
2223 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002224 if (dso)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03002225 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03002226 ", DSO: %s", dso->short_name);
Kan Liang84734b02015-09-04 10:45:45 -04002227 if (socket_id > -1)
Kan Liang21394d92015-09-04 10:45:44 -04002228 printed += scnprintf(bf + printed, size - printed,
Kan Liang84734b02015-09-04 10:45:45 -04002229 ", Processor Socket: %d", socket_id);
Taeung Song1e378eb2014-10-07 16:13:15 +09002230 if (!is_report_browser(hbt)) {
2231 struct perf_top *top = hbt->arg;
2232
2233 if (top->zero)
2234 printed += scnprintf(bf + printed, size - printed, " [z]");
2235 }
2236
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03002237 return printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002238}
2239
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002240static inline void free_popup_options(char **options, int n)
2241{
2242 int i;
2243
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -03002244 for (i = 0; i < n; ++i)
2245 zfree(&options[i]);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002246}
2247
Feng Tang341487ab2013-02-03 14:38:20 +08002248/*
2249 * Only runtime switching of perf data file will make "input_name" point
2250 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2251 * whether we need to call free() for current "input_name" during the switch.
2252 */
2253static bool is_input_name_malloced = false;
2254
2255static int switch_data_file(void)
2256{
2257 char *pwd, *options[32], *abs_path[32], *tmp;
2258 DIR *pwd_dir;
2259 int nr_options = 0, choice = -1, ret = -1;
2260 struct dirent *dent;
2261
2262 pwd = getenv("PWD");
2263 if (!pwd)
2264 return ret;
2265
2266 pwd_dir = opendir(pwd);
2267 if (!pwd_dir)
2268 return ret;
2269
2270 memset(options, 0, sizeof(options));
2271 memset(options, 0, sizeof(abs_path));
2272
2273 while ((dent = readdir(pwd_dir))) {
2274 char path[PATH_MAX];
2275 u64 magic;
2276 char *name = dent->d_name;
2277 FILE *file;
2278
2279 if (!(dent->d_type == DT_REG))
2280 continue;
2281
2282 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2283
2284 file = fopen(path, "r");
2285 if (!file)
2286 continue;
2287
2288 if (fread(&magic, 1, 8, file) < 8)
2289 goto close_file_and_continue;
2290
2291 if (is_perf_magic(magic)) {
2292 options[nr_options] = strdup(name);
2293 if (!options[nr_options])
2294 goto close_file_and_continue;
2295
2296 abs_path[nr_options] = strdup(path);
2297 if (!abs_path[nr_options]) {
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -03002298 zfree(&options[nr_options]);
Feng Tang341487ab2013-02-03 14:38:20 +08002299 ui__warning("Can't search all data files due to memory shortage.\n");
2300 fclose(file);
2301 break;
2302 }
2303
2304 nr_options++;
2305 }
2306
2307close_file_and_continue:
2308 fclose(file);
2309 if (nr_options >= 32) {
2310 ui__warning("Too many perf data files in PWD!\n"
2311 "Only the first 32 files will be listed.\n");
2312 break;
2313 }
2314 }
2315 closedir(pwd_dir);
2316
2317 if (nr_options) {
2318 choice = ui__popup_menu(nr_options, options);
2319 if (choice < nr_options && choice >= 0) {
2320 tmp = strdup(abs_path[choice]);
2321 if (tmp) {
2322 if (is_input_name_malloced)
2323 free((void *)input_name);
2324 input_name = tmp;
2325 is_input_name_malloced = true;
2326 ret = 0;
2327 } else
2328 ui__warning("Data switch failed due to memory shortage!\n");
2329 }
2330 }
2331
2332 free_popup_options(options, nr_options);
2333 free_popup_options(abs_path, nr_options);
2334 return ret;
2335}
2336
Namhyung Kimea7cd592015-04-22 16:18:19 +09002337struct popup_action {
2338 struct thread *thread;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002339 struct map_symbol ms;
Kan Liang84734b02015-09-04 10:45:45 -04002340 int socket;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002341
2342 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2343};
2344
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002345static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002346do_annotate(struct hist_browser *browser, struct popup_action *act)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002347{
2348 struct perf_evsel *evsel;
2349 struct annotation *notes;
2350 struct hist_entry *he;
2351 int err;
2352
Arnaldo Carvalho de Meloeebd0bf2015-09-08 15:52:20 -03002353 if (!objdump_path && perf_env__lookup_objdump(browser->env))
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002354 return 0;
2355
Namhyung Kimea7cd592015-04-22 16:18:19 +09002356 notes = symbol__annotation(act->ms.sym);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002357 if (!notes->src)
2358 return 0;
2359
2360 evsel = hists_to_evsel(browser->hists);
Namhyung Kimea7cd592015-04-22 16:18:19 +09002361 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002362 he = hist_browser__selected_entry(browser);
2363 /*
2364 * offer option to annotate the other branch source or target
2365 * (if they exists) when returning from annotate
2366 */
2367 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2368 return 1;
2369
2370 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2371 if (err)
2372 ui_browser__handle_resize(&browser->b);
2373 return 0;
2374}
2375
2376static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002377add_annotate_opt(struct hist_browser *browser __maybe_unused,
2378 struct popup_action *act, char **optstr,
2379 struct map *map, struct symbol *sym)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002380{
Namhyung Kimea7cd592015-04-22 16:18:19 +09002381 if (sym == NULL || map->dso->annotate_warned)
2382 return 0;
2383
2384 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2385 return 0;
2386
2387 act->ms.map = map;
2388 act->ms.sym = sym;
2389 act->fn = do_annotate;
2390 return 1;
2391}
2392
2393static int
2394do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2395{
2396 struct thread *thread = act->thread;
2397
Jiri Olsa7cecb7f2016-05-03 13:54:48 +02002398 if ((!hists__has(browser->hists, thread) &&
2399 !hists__has(browser->hists, comm)) || thread == NULL)
Namhyung Kim599a2f32016-03-09 23:20:53 +09002400 return 0;
2401
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002402 if (browser->hists->thread_filter) {
2403 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2404 perf_hpp__set_elide(HISTC_THREAD, false);
2405 thread__zput(browser->hists->thread_filter);
2406 ui_helpline__pop();
2407 } else {
Jiri Olsafa829112016-05-03 13:54:47 +02002408 if (hists__has(browser->hists, thread)) {
Namhyung Kim6962ccb2016-03-10 00:14:50 +09002409 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2410 thread->comm_set ? thread__comm_str(thread) : "",
2411 thread->tid);
2412 } else {
2413 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2414 thread->comm_set ? thread__comm_str(thread) : "");
2415 }
2416
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002417 browser->hists->thread_filter = thread__get(thread);
2418 perf_hpp__set_elide(HISTC_THREAD, false);
2419 pstack__push(browser->pstack, &browser->hists->thread_filter);
2420 }
2421
2422 hists__filter_by_thread(browser->hists);
2423 hist_browser__reset(browser);
2424 return 0;
2425}
2426
2427static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002428add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2429 char **optstr, struct thread *thread)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002430{
Namhyung Kim6962ccb2016-03-10 00:14:50 +09002431 int ret;
2432
Jiri Olsa7cecb7f2016-05-03 13:54:48 +02002433 if ((!hists__has(browser->hists, thread) &&
2434 !hists__has(browser->hists, comm)) || thread == NULL)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002435 return 0;
2436
Jiri Olsafa829112016-05-03 13:54:47 +02002437 if (hists__has(browser->hists, thread)) {
Namhyung Kim6962ccb2016-03-10 00:14:50 +09002438 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2439 browser->hists->thread_filter ? "out of" : "into",
2440 thread->comm_set ? thread__comm_str(thread) : "",
2441 thread->tid);
2442 } else {
2443 ret = asprintf(optstr, "Zoom %s %s thread",
2444 browser->hists->thread_filter ? "out of" : "into",
2445 thread->comm_set ? thread__comm_str(thread) : "");
2446 }
2447 if (ret < 0)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002448 return 0;
2449
2450 act->thread = thread;
2451 act->fn = do_zoom_thread;
2452 return 1;
2453}
2454
2455static int
2456do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2457{
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002458 struct map *map = act->ms.map;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002459
Jiri Olsa69849fc2016-05-03 13:54:45 +02002460 if (!hists__has(browser->hists, dso) || map == NULL)
Namhyung Kim599a2f32016-03-09 23:20:53 +09002461 return 0;
2462
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002463 if (browser->hists->dso_filter) {
2464 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2465 perf_hpp__set_elide(HISTC_DSO, false);
2466 browser->hists->dso_filter = NULL;
2467 ui_helpline__pop();
2468 } else {
Arnaldo Carvalho de Melo7727a922015-10-12 13:56:50 -03002469 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002470 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2471 browser->hists->dso_filter = map->dso;
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002472 perf_hpp__set_elide(HISTC_DSO, true);
2473 pstack__push(browser->pstack, &browser->hists->dso_filter);
2474 }
2475
2476 hists__filter_by_dso(browser->hists);
2477 hist_browser__reset(browser);
2478 return 0;
2479}
2480
2481static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002482add_dso_opt(struct hist_browser *browser, struct popup_action *act,
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002483 char **optstr, struct map *map)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002484{
Jiri Olsa69849fc2016-05-03 13:54:45 +02002485 if (!hists__has(browser->hists, dso) || map == NULL)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002486 return 0;
2487
2488 if (asprintf(optstr, "Zoom %s %s DSO",
2489 browser->hists->dso_filter ? "out of" : "into",
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002490 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002491 return 0;
2492
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002493 act->ms.map = map;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002494 act->fn = do_zoom_dso;
2495 return 1;
2496}
2497
2498static int
2499do_browse_map(struct hist_browser *browser __maybe_unused,
2500 struct popup_action *act)
2501{
2502 map__browse(act->ms.map);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002503 return 0;
2504}
2505
2506static int
Jiri Olsa69849fc2016-05-03 13:54:45 +02002507add_map_opt(struct hist_browser *browser,
Namhyung Kimea7cd592015-04-22 16:18:19 +09002508 struct popup_action *act, char **optstr, struct map *map)
2509{
Jiri Olsa69849fc2016-05-03 13:54:45 +02002510 if (!hists__has(browser->hists, dso) || map == NULL)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002511 return 0;
2512
2513 if (asprintf(optstr, "Browse map details") < 0)
2514 return 0;
2515
2516 act->ms.map = map;
2517 act->fn = do_browse_map;
2518 return 1;
2519}
2520
2521static int
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002522do_run_script(struct hist_browser *browser __maybe_unused,
Namhyung Kimea7cd592015-04-22 16:18:19 +09002523 struct popup_action *act)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002524{
2525 char script_opt[64];
2526 memset(script_opt, 0, sizeof(script_opt));
2527
Namhyung Kimea7cd592015-04-22 16:18:19 +09002528 if (act->thread) {
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002529 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
Namhyung Kimea7cd592015-04-22 16:18:19 +09002530 thread__comm_str(act->thread));
2531 } else if (act->ms.sym) {
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002532 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
Namhyung Kimea7cd592015-04-22 16:18:19 +09002533 act->ms.sym->name);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002534 }
2535
2536 script_browse(script_opt);
2537 return 0;
2538}
2539
2540static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002541add_script_opt(struct hist_browser *browser __maybe_unused,
2542 struct popup_action *act, char **optstr,
2543 struct thread *thread, struct symbol *sym)
2544{
2545 if (thread) {
2546 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2547 thread__comm_str(thread)) < 0)
2548 return 0;
2549 } else if (sym) {
2550 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2551 sym->name) < 0)
2552 return 0;
2553 } else {
2554 if (asprintf(optstr, "Run scripts for all samples") < 0)
2555 return 0;
2556 }
2557
2558 act->thread = thread;
2559 act->ms.sym = sym;
2560 act->fn = do_run_script;
2561 return 1;
2562}
2563
2564static int
2565do_switch_data(struct hist_browser *browser __maybe_unused,
2566 struct popup_action *act __maybe_unused)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002567{
2568 if (switch_data_file()) {
2569 ui__warning("Won't switch the data files due to\n"
2570 "no valid data file get selected!\n");
Namhyung Kimea7cd592015-04-22 16:18:19 +09002571 return 0;
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002572 }
2573
2574 return K_SWITCH_INPUT_DATA;
2575}
2576
Namhyung Kimea7cd592015-04-22 16:18:19 +09002577static int
2578add_switch_opt(struct hist_browser *browser,
2579 struct popup_action *act, char **optstr)
2580{
2581 if (!is_report_browser(browser->hbt))
2582 return 0;
2583
2584 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2585 return 0;
2586
2587 act->fn = do_switch_data;
2588 return 1;
2589}
2590
2591static int
2592do_exit_browser(struct hist_browser *browser __maybe_unused,
2593 struct popup_action *act __maybe_unused)
2594{
2595 return 0;
2596}
2597
2598static int
2599add_exit_opt(struct hist_browser *browser __maybe_unused,
2600 struct popup_action *act, char **optstr)
2601{
2602 if (asprintf(optstr, "Exit") < 0)
2603 return 0;
2604
2605 act->fn = do_exit_browser;
2606 return 1;
2607}
2608
Kan Liang84734b02015-09-04 10:45:45 -04002609static int
2610do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2611{
Jiri Olsa35a634f2016-05-03 13:54:46 +02002612 if (!hists__has(browser->hists, socket) || act->socket < 0)
Namhyung Kim599a2f32016-03-09 23:20:53 +09002613 return 0;
2614
Kan Liang84734b02015-09-04 10:45:45 -04002615 if (browser->hists->socket_filter > -1) {
2616 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2617 browser->hists->socket_filter = -1;
2618 perf_hpp__set_elide(HISTC_SOCKET, false);
2619 } else {
2620 browser->hists->socket_filter = act->socket;
2621 perf_hpp__set_elide(HISTC_SOCKET, true);
2622 pstack__push(browser->pstack, &browser->hists->socket_filter);
2623 }
2624
2625 hists__filter_by_socket(browser->hists);
2626 hist_browser__reset(browser);
2627 return 0;
2628}
2629
2630static int
2631add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2632 char **optstr, int socket_id)
2633{
Jiri Olsa35a634f2016-05-03 13:54:46 +02002634 if (!hists__has(browser->hists, socket) || socket_id < 0)
Kan Liang84734b02015-09-04 10:45:45 -04002635 return 0;
2636
2637 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2638 (browser->hists->socket_filter > -1) ? "out of" : "into",
2639 socket_id) < 0)
2640 return 0;
2641
2642 act->socket = socket_id;
2643 act->fn = do_zoom_socket;
2644 return 1;
2645}
2646
Namhyung Kim112f7612014-04-22 14:05:35 +09002647static void hist_browser__update_nr_entries(struct hist_browser *hb)
Namhyung Kim064f1982013-05-14 11:09:04 +09002648{
2649 u64 nr_entries = 0;
2650 struct rb_node *nd = rb_first(&hb->hists->entries);
2651
Namhyung Kimf5b763f2016-02-25 00:13:43 +09002652 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
Namhyung Kim268397c2014-04-22 14:49:31 +09002653 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2654 return;
2655 }
2656
Namhyung Kim14135662013-10-31 10:17:39 +09002657 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
Namhyung Kim064f1982013-05-14 11:09:04 +09002658 nr_entries++;
Namhyung Kimf5b763f2016-02-25 00:13:43 +09002659 nd = rb_hierarchy_next(nd);
Namhyung Kim064f1982013-05-14 11:09:04 +09002660 }
2661
Namhyung Kim112f7612014-04-22 14:05:35 +09002662 hb->nr_non_filtered_entries = nr_entries;
Namhyung Kimf5b763f2016-02-25 00:13:43 +09002663 hb->nr_hierarchy_entries = nr_entries;
Namhyung Kim064f1982013-05-14 11:09:04 +09002664}
Feng Tang341487ab2013-02-03 14:38:20 +08002665
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002666static void hist_browser__update_percent_limit(struct hist_browser *hb,
2667 double percent)
2668{
2669 struct hist_entry *he;
2670 struct rb_node *nd = rb_first(&hb->hists->entries);
2671 u64 total = hists__total_period(hb->hists);
2672 u64 min_callchain_hits = total * (percent / 100);
2673
2674 hb->min_pcnt = callchain_param.min_percent = percent;
2675
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002676 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2677 he = rb_entry(nd, struct hist_entry, rb_node);
2678
Namhyung Kim79dded82016-02-26 21:13:19 +09002679 if (he->has_no_entry) {
2680 he->has_no_entry = false;
2681 he->nr_rows = 0;
2682 }
2683
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002684 if (!he->leaf || !symbol_conf.use_callchain)
2685 goto next;
2686
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002687 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2688 total = he->stat.period;
2689
2690 if (symbol_conf.cumulate_callchain)
2691 total = he->stat_acc->period;
2692
2693 min_callchain_hits = total * (percent / 100);
2694 }
2695
2696 callchain_param.sort(&he->sorted_chain, he->callchain,
2697 min_callchain_hits, &callchain_param);
2698
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002699next:
Namhyung Kim201fde72016-02-26 21:13:18 +09002700 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002701
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002702 /* force to re-evaluate folding state of callchains */
2703 he->init_have_children = false;
Namhyung Kim492b1012016-02-25 00:13:44 +09002704 hist_entry__set_folding(he, hb, false);
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002705 }
2706}
2707
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03002708static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
Jiri Olsadd00d482014-06-19 13:41:13 +02002709 const char *helpline,
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03002710 bool left_exits,
Namhyung Kim68d80752012-11-02 14:50:06 +09002711 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09002712 float min_pcnt,
Kan Liangce80d3b2015-08-28 05:48:04 -04002713 struct perf_env *env)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002714{
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002715 struct hists *hists = evsel__hists(evsel);
Jiri Olsaa6ec8942016-06-20 23:58:17 +02002716 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002717 struct branch_info *bi;
Namhyung Kimf2b487d2015-04-22 16:18:14 +09002718#define MAX_OPTIONS 16
2719 char *options[MAX_OPTIONS];
Namhyung Kimea7cd592015-04-22 16:18:19 +09002720 struct popup_action actions[MAX_OPTIONS];
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002721 int nr_options = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002722 int key = -1;
Namhyung Kim938a23a2012-03-16 17:50:53 +09002723 char buf[64];
Namhyung Kim9783adf2012-11-02 14:50:05 +09002724 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002725
Namhyung Kime8e684a2013-12-26 14:37:58 +09002726#define HIST_BROWSER_HELP_COMMON \
2727 "h/?/F1 Show this window\n" \
2728 "UP/DOWN/PGUP\n" \
2729 "PGDN/SPACE Navigate\n" \
2730 "q/ESC/CTRL+C Exit browser\n\n" \
2731 "For multiple event sessions:\n\n" \
2732 "TAB/UNTAB Switch events\n\n" \
2733 "For symbolic views (--sort has sym):\n\n" \
Arnaldo Carvalho de Melo7727a922015-10-12 13:56:50 -03002734 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2735 "ESC Zoom out\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09002736 "a Annotate current symbol\n" \
2737 "C Collapse all callchains\n" \
2738 "d Zoom into current DSO\n" \
2739 "E Expand all callchains\n" \
Namhyung Kim105eb302014-02-10 11:20:10 +09002740 "F Toggle percentage of filtered entries\n" \
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02002741 "H Display column headers\n" \
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002742 "L Change percent limit\n" \
Namhyung Kim31eb4362015-10-13 09:02:01 +09002743 "m Display context menu\n" \
Kan Liang84734b02015-09-04 10:45:45 -04002744 "S Zoom into current Processor Socket\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09002745
2746 /* help messages are sorted by lexical order of the hotkey */
2747 const char report_help[] = HIST_BROWSER_HELP_COMMON
Namhyung Kim6dd60132013-12-26 14:37:59 +09002748 "i Show header information\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09002749 "P Print histograms to perf.hist.N\n"
2750 "r Run available scripts\n"
2751 "s Switch to another data file in PWD\n"
2752 "t Zoom into current Thread\n"
2753 "V Verbose (DSO names in callchains, etc)\n"
2754 "/ Filter symbol by name";
2755 const char top_help[] = HIST_BROWSER_HELP_COMMON
2756 "P Print histograms to perf.hist.N\n"
2757 "t Zoom into current Thread\n"
2758 "V Verbose (DSO names in callchains, etc)\n"
Namhyung Kim42337a22014-08-12 17:16:06 +09002759 "z Toggle zeroing of samples\n"
Arnaldo Carvalho de Melofbb79972015-06-19 16:56:04 -03002760 "f Enable/Disable events\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09002761 "/ Filter symbol by name";
2762
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002763 if (browser == NULL)
2764 return -1;
2765
Namhyung Kimed426912015-05-29 21:53:44 +09002766 /* reset abort key so that it can get Ctrl-C as a key */
2767 SLang_reset_tty();
2768 SLang_init_tty(0, 0, 0);
2769
Namhyung Kim03905042015-11-28 02:32:39 +09002770 if (min_pcnt)
Namhyung Kim064f1982013-05-14 11:09:04 +09002771 browser->min_pcnt = min_pcnt;
Namhyung Kim03905042015-11-28 02:32:39 +09002772 hist_browser__update_nr_entries(browser);
Namhyung Kim064f1982013-05-14 11:09:04 +09002773
Kan Liang84734b02015-09-04 10:45:45 -04002774 browser->pstack = pstack__new(3);
Namhyung Kim01f00a12015-04-22 16:18:16 +09002775 if (browser->pstack == NULL)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002776 goto out;
2777
2778 ui_helpline__push(helpline);
2779
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002780 memset(options, 0, sizeof(options));
Namhyung Kimea7cd592015-04-22 16:18:19 +09002781 memset(actions, 0, sizeof(actions));
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002782
Namhyung Kim5b591662014-07-31 14:47:38 +09002783 if (symbol_conf.col_width_list_str)
2784 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2785
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002786 while (1) {
Arnaldo Carvalho de Melof3b623b2015-03-02 22:21:35 -03002787 struct thread *thread = NULL;
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002788 struct map *map = NULL;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002789 int choice = 0;
Kan Liang84734b02015-09-04 10:45:45 -04002790 int socked_id = -1;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002791
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002792 nr_options = 0;
2793
Arnaldo Carvalho de Melo5f00b0f2015-06-19 17:30:20 -03002794 key = hist_browser__run(browser, helpline);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002795
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03002796 if (browser->he_selection != NULL) {
2797 thread = hist_browser__selected_thread(browser);
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002798 map = browser->selection->map;
Kan Liang84734b02015-09-04 10:45:45 -04002799 socked_id = browser->he_selection->socket;
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03002800 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002801 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002802 case K_TAB:
2803 case K_UNTAB:
David Aherne4419b82011-10-19 11:37:47 -06002804 if (nr_events == 1)
2805 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002806 /*
2807 * Exit the browser, let hists__browser_tree
2808 * go to the next or previous
2809 */
2810 goto out_free_stack;
2811 case 'a':
Jiri Olsa2e0453a2016-05-03 13:54:44 +02002812 if (!hists__has(hists, sym)) {
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002813 ui_browser__warning(&browser->b, delay_secs * 2,
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02002814 "Annotation is only available for symbolic views, "
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002815 "include \"sym*\" in --sort to use it.");
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02002816 continue;
2817 }
2818
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03002819 if (browser->selection == NULL ||
Lin Mingdb9a9cbc2011-04-08 14:31:26 +08002820 browser->selection->sym == NULL ||
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002821 browser->selection->map->dso->annotate_warned)
2822 continue;
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002823
Namhyung Kimea7cd592015-04-22 16:18:19 +09002824 actions->ms.map = browser->selection->map;
2825 actions->ms.sym = browser->selection->sym;
2826 do_annotate(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002827 continue;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03002828 case 'P':
2829 hist_browser__dump(browser);
2830 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002831 case 'd':
Arnaldo Carvalho de Melofae00652015-11-12 15:59:26 -03002832 actions->ms.map = map;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002833 do_zoom_dso(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002834 continue;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03002835 case 'V':
2836 browser->show_dso = !browser->show_dso;
2837 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002838 case 't':
Namhyung Kimea7cd592015-04-22 16:18:19 +09002839 actions->thread = thread;
2840 do_zoom_thread(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002841 continue;
Kan Liang84734b02015-09-04 10:45:45 -04002842 case 'S':
2843 actions->socket = socked_id;
2844 do_zoom_socket(browser, actions);
2845 continue;
Arnaldo Carvalho de Melo5a5626b2012-05-09 12:21:22 -03002846 case '/':
Namhyung Kim938a23a2012-03-16 17:50:53 +09002847 if (ui_browser__input_window("Symbol to show",
Arnaldo Carvalho de Melo4aa8e452015-10-12 14:02:29 -03002848 "Please enter the name of symbol you want to see.\n"
2849 "To remove the filter later, press / + ENTER.",
Namhyung Kim938a23a2012-03-16 17:50:53 +09002850 buf, "ENTER: OK, ESC: Cancel",
2851 delay_secs * 2) == K_ENTER) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002852 hists->symbol_filter_str = *buf ? buf : NULL;
2853 hists__filter_by_symbol(hists);
Namhyung Kim938a23a2012-03-16 17:50:53 +09002854 hist_browser__reset(browser);
2855 }
2856 continue;
Feng Tangcdbab7c2012-10-30 11:56:06 +08002857 case 'r':
Namhyung Kimea7cd592015-04-22 16:18:19 +09002858 if (is_report_browser(hbt)) {
2859 actions->thread = NULL;
2860 actions->ms.sym = NULL;
2861 do_run_script(browser, actions);
2862 }
Feng Tangc77d8d72012-11-01 00:00:55 +08002863 continue;
Feng Tang341487ab2013-02-03 14:38:20 +08002864 case 's':
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002865 if (is_report_browser(hbt)) {
Namhyung Kimea7cd592015-04-22 16:18:19 +09002866 key = do_switch_data(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002867 if (key == K_SWITCH_INPUT_DATA)
2868 goto out_free_stack;
2869 }
Feng Tang341487ab2013-02-03 14:38:20 +08002870 continue;
Namhyung Kim6dd60132013-12-26 14:37:59 +09002871 case 'i':
2872 /* env->arch is NULL for live-mode (i.e. perf top) */
2873 if (env->arch)
2874 tui__header_window(env);
2875 continue;
Namhyung Kim105eb302014-02-10 11:20:10 +09002876 case 'F':
2877 symbol_conf.filter_relative ^= 1;
2878 continue;
Namhyung Kim42337a22014-08-12 17:16:06 +09002879 case 'z':
2880 if (!is_report_browser(hbt)) {
2881 struct perf_top *top = hbt->arg;
2882
2883 top->zero = !top->zero;
2884 }
2885 continue;
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002886 case 'L':
2887 if (ui_browser__input_window("Percent Limit",
2888 "Please enter the value you want to hide entries under that percent.",
2889 buf, "ENTER: OK, ESC: Cancel",
2890 delay_secs * 2) == K_ENTER) {
2891 char *end;
2892 double new_percent = strtod(buf, &end);
2893
2894 if (new_percent < 0 || new_percent > 100) {
2895 ui_browser__warning(&browser->b, delay_secs * 2,
2896 "Invalid percent: %.2f", new_percent);
2897 continue;
2898 }
2899
2900 hist_browser__update_percent_limit(browser, new_percent);
2901 hist_browser__reset(browser);
2902 }
2903 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002904 case K_F1:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002905 case 'h':
2906 case '?':
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02002907 ui_browser__help_window(&browser->b,
Namhyung Kime8e684a2013-12-26 14:37:58 +09002908 is_report_browser(hbt) ? report_help : top_help);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002909 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002910 case K_ENTER:
2911 case K_RIGHT:
Namhyung Kim31eb4362015-10-13 09:02:01 +09002912 case 'm':
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002913 /* menu */
2914 break;
Arnaldo Carvalho de Melo63ab1742015-08-12 12:42:58 -03002915 case K_ESC:
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002916 case K_LEFT: {
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002917 const void *top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002918
Namhyung Kim01f00a12015-04-22 16:18:16 +09002919 if (pstack__empty(browser->pstack)) {
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002920 /*
2921 * Go back to the perf_evsel_menu__run or other user
2922 */
2923 if (left_exits)
2924 goto out_free_stack;
Arnaldo Carvalho de Melo63ab1742015-08-12 12:42:58 -03002925
2926 if (key == K_ESC &&
2927 ui_browser__dialog_yesno(&browser->b,
2928 "Do you really want to exit?"))
2929 goto out_free_stack;
2930
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002931 continue;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002932 }
Namhyung Kim64221842015-04-24 10:15:33 +09002933 top = pstack__peek(browser->pstack);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002934 if (top == &browser->hists->dso_filter) {
Namhyung Kim64221842015-04-24 10:15:33 +09002935 /*
2936 * No need to set actions->dso here since
2937 * it's just to remove the current filter.
2938 * Ditto for thread below.
2939 */
2940 do_zoom_dso(browser, actions);
Kan Liang84734b02015-09-04 10:45:45 -04002941 } else if (top == &browser->hists->thread_filter) {
Namhyung Kim64221842015-04-24 10:15:33 +09002942 do_zoom_thread(browser, actions);
Kan Liang84734b02015-09-04 10:45:45 -04002943 } else if (top == &browser->hists->socket_filter) {
2944 do_zoom_socket(browser, actions);
2945 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002946 continue;
2947 }
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002948 case 'q':
2949 case CTRL('c'):
Arnaldo Carvalho de Melo516e5362015-06-19 16:59:43 -03002950 goto out_free_stack;
Arnaldo Carvalho de Melofbb79972015-06-19 16:56:04 -03002951 case 'f':
Namhyung Kim13d1e532015-06-21 12:41:16 +09002952 if (!is_report_browser(hbt)) {
2953 struct perf_top *top = hbt->arg;
2954
2955 perf_evlist__toggle_enable(top->evlist);
2956 /*
2957 * No need to refresh, resort/decay histogram
2958 * entries if we are not collecting samples:
2959 */
2960 if (top->evlist->enabled) {
2961 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2962 hbt->refresh = delay_secs;
2963 } else {
2964 helpline = "Press 'f' again to re-enable the events";
2965 hbt->refresh = 0;
2966 }
2967 continue;
2968 }
Arnaldo Carvalho de Melo3e323dc2015-06-19 17:49:29 -03002969 /* Fall thru */
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002970 default:
Arnaldo Carvalho de Melo3e323dc2015-06-19 17:49:29 -03002971 helpline = "Press '?' for help on key bindings";
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002972 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002973 }
2974
Jiri Olsa2e0453a2016-05-03 13:54:44 +02002975 if (!hists__has(hists, sym) || browser->selection == NULL)
Arnaldo Carvalho de Melo0ba332f2015-03-16 17:41:52 -03002976 goto skip_annotation;
2977
Namhyung Kim55369fc2013-04-01 20:35:20 +09002978 if (sort__mode == SORT_MODE__BRANCH) {
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002979 bi = browser->he_selection->branch_info;
Arnaldo Carvalho de Melo0ba332f2015-03-16 17:41:52 -03002980
2981 if (bi == NULL)
2982 goto skip_annotation;
2983
Namhyung Kimea7cd592015-04-22 16:18:19 +09002984 nr_options += add_annotate_opt(browser,
2985 &actions[nr_options],
2986 &options[nr_options],
2987 bi->from.map,
2988 bi->from.sym);
2989 if (bi->to.sym != bi->from.sym)
2990 nr_options += add_annotate_opt(browser,
2991 &actions[nr_options],
2992 &options[nr_options],
2993 bi->to.map,
2994 bi->to.sym);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002995 } else {
Namhyung Kimea7cd592015-04-22 16:18:19 +09002996 nr_options += add_annotate_opt(browser,
2997 &actions[nr_options],
2998 &options[nr_options],
2999 browser->selection->map,
3000 browser->selection->sym);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01003001 }
Arnaldo Carvalho de Melo0ba332f2015-03-16 17:41:52 -03003002skip_annotation:
Namhyung Kimea7cd592015-04-22 16:18:19 +09003003 nr_options += add_thread_opt(browser, &actions[nr_options],
3004 &options[nr_options], thread);
3005 nr_options += add_dso_opt(browser, &actions[nr_options],
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03003006 &options[nr_options], map);
Namhyung Kimea7cd592015-04-22 16:18:19 +09003007 nr_options += add_map_opt(browser, &actions[nr_options],
3008 &options[nr_options],
Wang Nanbd315aa2015-09-14 10:23:55 +00003009 browser->selection ?
3010 browser->selection->map : NULL);
Kan Liang84734b02015-09-04 10:45:45 -04003011 nr_options += add_socket_opt(browser, &actions[nr_options],
3012 &options[nr_options],
3013 socked_id);
Feng Tangcdbab7c2012-10-30 11:56:06 +08003014 /* perf script support */
Namhyung Kimb1baae82016-01-26 15:37:30 -03003015 if (!is_report_browser(hbt))
3016 goto skip_scripting;
3017
Feng Tangcdbab7c2012-10-30 11:56:06 +08003018 if (browser->he_selection) {
Jiri Olsafa829112016-05-03 13:54:47 +02003019 if (hists__has(hists, thread) && thread) {
Namhyung Kim2eafd412016-01-21 19:13:24 -03003020 nr_options += add_script_opt(browser,
3021 &actions[nr_options],
3022 &options[nr_options],
3023 thread, NULL);
3024 }
Wang Nanbd315aa2015-09-14 10:23:55 +00003025 /*
3026 * Note that browser->selection != NULL
3027 * when browser->he_selection is not NULL,
3028 * so we don't need to check browser->selection
3029 * before fetching browser->selection->sym like what
3030 * we do before fetching browser->selection->map.
3031 *
3032 * See hist_browser__show_entry.
3033 */
Jiri Olsa2e0453a2016-05-03 13:54:44 +02003034 if (hists__has(hists, sym) && browser->selection->sym) {
Namhyung Kimc221acb2016-01-21 19:50:09 -03003035 nr_options += add_script_opt(browser,
3036 &actions[nr_options],
3037 &options[nr_options],
3038 NULL, browser->selection->sym);
3039 }
Feng Tangcdbab7c2012-10-30 11:56:06 +08003040 }
Namhyung Kimea7cd592015-04-22 16:18:19 +09003041 nr_options += add_script_opt(browser, &actions[nr_options],
3042 &options[nr_options], NULL, NULL);
3043 nr_options += add_switch_opt(browser, &actions[nr_options],
3044 &options[nr_options]);
Namhyung Kimb1baae82016-01-26 15:37:30 -03003045skip_scripting:
Namhyung Kimea7cd592015-04-22 16:18:19 +09003046 nr_options += add_exit_opt(browser, &actions[nr_options],
3047 &options[nr_options]);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003048
Namhyung Kimea7cd592015-04-22 16:18:19 +09003049 do {
3050 struct popup_action *act;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003051
Namhyung Kimea7cd592015-04-22 16:18:19 +09003052 choice = ui__popup_menu(nr_options, options);
3053 if (choice == -1 || choice >= nr_options)
Feng Tang341487ab2013-02-03 14:38:20 +08003054 break;
Namhyung Kimea7cd592015-04-22 16:18:19 +09003055
3056 act = &actions[choice];
3057 key = act->fn(browser, act);
3058 } while (key == 1);
3059
3060 if (key == K_SWITCH_INPUT_DATA)
3061 break;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003062 }
3063out_free_stack:
Namhyung Kim01f00a12015-04-22 16:18:16 +09003064 pstack__delete(browser->pstack);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003065out:
3066 hist_browser__delete(browser);
Namhyung Kimf2b487d2015-04-22 16:18:14 +09003067 free_popup_options(options, MAX_OPTIONS);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003068 return key;
3069}
3070
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003071struct perf_evsel_menu {
3072 struct ui_browser b;
3073 struct perf_evsel *selection;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003074 bool lost_events, lost_events_warned;
Namhyung Kim064f1982013-05-14 11:09:04 +09003075 float min_pcnt;
Kan Liangce80d3b2015-08-28 05:48:04 -04003076 struct perf_env *env;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003077};
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003078
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003079static void perf_evsel_menu__write(struct ui_browser *browser,
3080 void *entry, int row)
3081{
3082 struct perf_evsel_menu *menu = container_of(browser,
3083 struct perf_evsel_menu, b);
3084 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03003085 struct hists *hists = evsel__hists(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003086 bool current_entry = ui_browser__is_current_entry(browser, row);
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03003087 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03003088 const char *ev_name = perf_evsel__name(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003089 char bf[256], unit;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003090 const char *warn = " ";
3091 size_t printed;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003092
3093 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3094 HE_COLORSET_NORMAL);
3095
Namhyung Kim759ff492013-03-05 14:53:26 +09003096 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09003097 struct perf_evsel *pos;
3098
3099 ev_name = perf_evsel__group_name(evsel);
3100
3101 for_each_group_member(pos, evsel) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03003102 struct hists *pos_hists = evsel__hists(pos);
3103 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
Namhyung Kim717e2632013-01-22 18:09:44 +09003104 }
3105 }
3106
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003107 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03003108 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003109 unit, unit == ' ' ? "" : " ", ev_name);
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03003110 ui_browser__printf(browser, "%s", bf);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003111
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03003112 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003113 if (nr_events != 0) {
3114 menu->lost_events = true;
3115 if (!current_entry)
3116 ui_browser__set_color(browser, HE_COLORSET_TOP);
3117 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03003118 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3119 nr_events, unit, unit == ' ' ? "" : " ");
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003120 warn = bf;
3121 }
3122
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -03003123 ui_browser__write_nstring(browser, warn, browser->width - printed);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003124
3125 if (current_entry)
3126 menu->selection = evsel;
3127}
3128
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03003129static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3130 int nr_events, const char *help,
Namhyung Kim9783adf2012-11-02 14:50:05 +09003131 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003132{
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003133 struct perf_evlist *evlist = menu->b.priv;
3134 struct perf_evsel *pos;
Jiri Olsadd00d482014-06-19 13:41:13 +02003135 const char *title = "Available samples";
Namhyung Kim9783adf2012-11-02 14:50:05 +09003136 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003137 int key;
3138
3139 if (ui_browser__show(&menu->b, title,
3140 "ESC: exit, ENTER|->: Browse histograms") < 0)
3141 return -1;
3142
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003143 while (1) {
Arnaldo Carvalho de Melo3af6e332011-10-13 08:52:46 -03003144 key = ui_browser__run(&menu->b, delay_secs);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003145
3146 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02003147 case K_TIMER:
Namhyung Kim9783adf2012-11-02 14:50:05 +09003148 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02003149
3150 if (!menu->lost_events_warned && menu->lost_events) {
3151 ui_browser__warn_lost_events(&menu->b);
3152 menu->lost_events_warned = true;
3153 }
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03003154 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02003155 case K_RIGHT:
3156 case K_ENTER:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003157 if (!menu->selection)
3158 continue;
3159 pos = menu->selection;
3160browse_hists:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003161 perf_evlist__set_selected(evlist, pos);
3162 /*
3163 * Give the calling tool a chance to populate the non
3164 * default evsel resorted hists tree.
3165 */
Namhyung Kim9783adf2012-11-02 14:50:05 +09003166 if (hbt)
3167 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03003168 key = perf_evsel__hists_browse(pos, nr_events, help,
Jiri Olsadd00d482014-06-19 13:41:13 +02003169 true, hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09003170 menu->min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09003171 menu->env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003172 ui_browser__show_title(&menu->b, title);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003173 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02003174 case K_TAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003175 if (pos->node.next == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03003176 pos = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003177 else
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03003178 pos = perf_evsel__next(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003179 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02003180 case K_UNTAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003181 if (pos->node.prev == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03003182 pos = perf_evlist__last(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003183 else
Arnaldo Carvalho de Melod87fcb42013-11-13 15:56:40 -03003184 pos = perf_evsel__prev(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003185 goto browse_hists;
Feng Tang341487ab2013-02-03 14:38:20 +08003186 case K_SWITCH_INPUT_DATA:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003187 case 'q':
3188 case CTRL('c'):
3189 goto out;
Arnaldo Carvalho de Melo63ab1742015-08-12 12:42:58 -03003190 case K_ESC:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003191 default:
3192 continue;
3193 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02003194 case K_LEFT:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003195 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02003196 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02003197 if (!ui_browser__dialog_yesno(&menu->b,
3198 "Do you really want to exit?"))
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03003199 continue;
3200 /* Fall thru */
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003201 case 'q':
3202 case CTRL('c'):
3203 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003204 default:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03003205 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003206 }
3207 }
3208
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003209out:
3210 ui_browser__hide(&menu->b);
3211 return key;
3212}
3213
Arnaldo Carvalho de Melo316c7132013-11-05 15:32:36 -03003214static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003215 void *entry)
3216{
3217 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3218
3219 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3220 return true;
3221
3222 return false;
3223}
3224
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003225static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003226 int nr_entries, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09003227 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09003228 float min_pcnt,
Kan Liangce80d3b2015-08-28 05:48:04 -04003229 struct perf_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003230{
3231 struct perf_evsel *pos;
3232 struct perf_evsel_menu menu = {
3233 .b = {
3234 .entries = &evlist->entries,
3235 .refresh = ui_browser__list_head_refresh,
3236 .seek = ui_browser__list_head_seek,
3237 .write = perf_evsel_menu__write,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003238 .filter = filter_group_entries,
3239 .nr_entries = nr_entries,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003240 .priv = evlist,
3241 },
Namhyung Kim064f1982013-05-14 11:09:04 +09003242 .min_pcnt = min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09003243 .env = env,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003244 };
3245
3246 ui_helpline__push("Press ESC to exit");
3247
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -03003248 evlist__for_each_entry(evlist, pos) {
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03003249 const char *ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003250 size_t line_len = strlen(ev_name) + 7;
3251
3252 if (menu.b.width < line_len)
3253 menu.b.width = line_len;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003254 }
3255
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003256 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003257}
3258
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03003259int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09003260 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09003261 float min_pcnt,
Kan Liangce80d3b2015-08-28 05:48:04 -04003262 struct perf_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003263{
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003264 int nr_entries = evlist->nr_entries;
3265
3266single_entry:
3267 if (nr_entries == 1) {
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03003268 struct perf_evsel *first = perf_evlist__first(evlist);
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003269
3270 return perf_evsel__hists_browse(first, nr_entries, help,
Jiri Olsadd00d482014-06-19 13:41:13 +02003271 false, hbt, min_pcnt,
Namhyung Kim064f1982013-05-14 11:09:04 +09003272 env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003273 }
3274
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003275 if (symbol_conf.event_group) {
3276 struct perf_evsel *pos;
3277
3278 nr_entries = 0;
Arnaldo Carvalho de Meloe5cadb92016-06-23 11:26:15 -03003279 evlist__for_each_entry(evlist, pos) {
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003280 if (perf_evsel__is_group_leader(pos))
3281 nr_entries++;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03003282 }
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003283
3284 if (nr_entries == 1)
3285 goto single_entry;
3286 }
3287
3288 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09003289 hbt, min_pcnt, env);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003290}