blob: 2bccf68ce5f10d881adca9799d2411f86bde9719 [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
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -020018#include "../ui.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030019#include "map.h"
Jiri Olsad7553302014-06-15 10:22:15 +020020#include "annotate.h"
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030021
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030022struct hist_browser {
23 struct ui_browser b;
24 struct hists *hists;
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
Namhyung Kimc2a51ab2015-04-22 16:18:15 +090027 struct hist_browser_timer *hbt;
Namhyung Kim01f00a12015-04-22 16:18:16 +090028 struct pstack *pstack;
Kan Liangce80d3b2015-08-28 05:48:04 -040029 struct perf_env *env;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -030030 int print_seq;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -030031 bool show_dso;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +020032 bool show_headers;
Namhyung Kim064f1982013-05-14 11:09:04 +090033 float min_pcnt;
Namhyung Kim112f7612014-04-22 14:05:35 +090034 u64 nr_non_filtered_entries;
Namhyung Kimf5b763f2016-02-25 00:13:43 +090035 u64 nr_hierarchy_entries;
Namhyung Kimc3b78952014-04-22 15:56:17 +090036 u64 nr_callchain_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -030037};
38
Namhyung Kimf5951d52012-09-03 11:53:09 +090039extern void hist_browser__init_hpp(void);
40
Taeung Song1e378eb2014-10-07 16:13:15 +090041static int hists__browser_title(struct hists *hists,
42 struct hist_browser_timer *hbt,
43 char *bf, size_t size);
Namhyung Kim112f7612014-04-22 14:05:35 +090044static void hist_browser__update_nr_entries(struct hist_browser *hb);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -030045
Namhyung Kimc3b78952014-04-22 15:56:17 +090046static struct rb_node *hists__filter_entries(struct rb_node *nd,
Namhyung Kimc3b78952014-04-22 15:56:17 +090047 float min_pcnt);
48
Namhyung Kim268397c2014-04-22 14:49:31 +090049static bool hist_browser__has_filter(struct hist_browser *hb)
50{
Arnaldo Carvalho de Melo9c0fa8d2015-07-13 08:26:35 -030051 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
Namhyung Kim268397c2014-04-22 14:49:31 +090052}
53
He Kuang4fabf3d2015-03-12 15:21:49 +080054static int hist_browser__get_folding(struct hist_browser *browser)
55{
56 struct rb_node *nd;
57 struct hists *hists = browser->hists;
58 int unfolded_rows = 0;
59
60 for (nd = rb_first(&hists->entries);
61 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
Namhyung Kimf5b763f2016-02-25 00:13:43 +090062 nd = rb_hierarchy_next(nd)) {
He Kuang4fabf3d2015-03-12 15:21:49 +080063 struct hist_entry *he =
64 rb_entry(nd, struct hist_entry, rb_node);
65
Namhyung Kimf5b763f2016-02-25 00:13:43 +090066 if (he->leaf && he->unfolded)
He Kuang4fabf3d2015-03-12 15:21:49 +080067 unfolded_rows += he->nr_rows;
68 }
69 return unfolded_rows;
70}
71
Namhyung Kimc3b78952014-04-22 15:56:17 +090072static u32 hist_browser__nr_entries(struct hist_browser *hb)
73{
74 u32 nr_entries;
75
Namhyung Kimf5b763f2016-02-25 00:13:43 +090076 if (symbol_conf.report_hierarchy)
77 nr_entries = hb->nr_hierarchy_entries;
78 else if (hist_browser__has_filter(hb))
Namhyung Kimc3b78952014-04-22 15:56:17 +090079 nr_entries = hb->nr_non_filtered_entries;
80 else
81 nr_entries = hb->hists->nr_entries;
82
He Kuang4fabf3d2015-03-12 15:21:49 +080083 hb->nr_callchain_rows = hist_browser__get_folding(hb);
Namhyung Kimc3b78952014-04-22 15:56:17 +090084 return nr_entries + hb->nr_callchain_rows;
85}
86
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +020087static void hist_browser__update_rows(struct hist_browser *hb)
88{
89 struct ui_browser *browser = &hb->b;
90 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
91
92 browser->rows = browser->height - header_offset;
93 /*
94 * Verify if we were at the last line and that line isn't
95 * visibe because we now show the header line(s).
96 */
97 index_row = browser->index - browser->top_idx;
98 if (index_row >= browser->rows)
99 browser->index -= index_row - browser->rows + 1;
100}
101
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -0300102static void hist_browser__refresh_dimensions(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300103{
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -0300104 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
105
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300106 /* 3 == +/- toggle symbol before actual hist_entry rendering */
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -0300107 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
108 /*
109 * FIXME: Just keeping existing behaviour, but this really should be
110 * before updating browser->width, as it will invalidate the
111 * calculation above. Fix this and the fallout in another
112 * changeset.
113 */
114 ui_browser__refresh_dimensions(browser);
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200115 hist_browser__update_rows(hb);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300116}
117
Arnaldo Carvalho de Meloca3ff332014-07-01 16:42:24 -0300118static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
119{
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200120 u16 header_offset = browser->show_headers ? 1 : 0;
121
122 ui_browser__gotorc(&browser->b, row + header_offset, column);
Arnaldo Carvalho de Meloca3ff332014-07-01 16:42:24 -0300123}
124
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300125static void hist_browser__reset(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300126{
Namhyung Kimc3b78952014-04-22 15:56:17 +0900127 /*
128 * The hists__remove_entry_filter() already folds non-filtered
129 * entries so we can assume it has 0 callchain rows.
130 */
131 browser->nr_callchain_rows = 0;
132
Namhyung Kim268397c2014-04-22 14:49:31 +0900133 hist_browser__update_nr_entries(browser);
Namhyung Kimc3b78952014-04-22 15:56:17 +0900134 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -0300135 hist_browser__refresh_dimensions(&browser->b);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300136 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300137}
138
139static char tree__folded_sign(bool unfolded)
140{
141 return unfolded ? '-' : '+';
142}
143
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300144static char hist_entry__folded(const struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300145{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900146 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300147}
148
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300149static char callchain_list__folded(const struct callchain_list *cl)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300150{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900151 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300152}
153
Namhyung Kim3698dab2015-05-05 23:55:46 +0900154static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300155{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900156 cl->unfolded = unfold ? cl->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300157}
158
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300159static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300160{
161 int n = 0;
162 struct rb_node *nd;
163
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300164 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300165 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
166 struct callchain_list *chain;
167 char folded_sign = ' '; /* No children */
168
169 list_for_each_entry(chain, &child->val, list) {
170 ++n;
171 /* We need this because we may not have children */
172 folded_sign = callchain_list__folded(chain);
173 if (folded_sign == '+')
174 break;
175 }
176
177 if (folded_sign == '-') /* Have children and they're unfolded */
178 n += callchain_node__count_rows_rb_tree(child);
179 }
180
181 return n;
182}
183
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900184static int callchain_node__count_flat_rows(struct callchain_node *node)
185{
186 struct callchain_list *chain;
187 char folded_sign = 0;
188 int n = 0;
189
190 list_for_each_entry(chain, &node->parent_val, list) {
191 if (!folded_sign) {
192 /* only check first chain list entry */
193 folded_sign = callchain_list__folded(chain);
194 if (folded_sign == '+')
195 return 1;
196 }
197 n++;
198 }
199
200 list_for_each_entry(chain, &node->val, list) {
201 if (!folded_sign) {
202 /* node->parent_val list might be empty */
203 folded_sign = callchain_list__folded(chain);
204 if (folded_sign == '+')
205 return 1;
206 }
207 n++;
208 }
209
210 return n;
211}
212
Namhyung Kim8c430a32015-11-09 14:45:44 +0900213static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
214{
215 return 1;
216}
217
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300218static int callchain_node__count_rows(struct callchain_node *node)
219{
220 struct callchain_list *chain;
221 bool unfolded = false;
222 int n = 0;
223
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900224 if (callchain_param.mode == CHAIN_FLAT)
225 return callchain_node__count_flat_rows(node);
Namhyung Kim8c430a32015-11-09 14:45:44 +0900226 else if (callchain_param.mode == CHAIN_FOLDED)
227 return callchain_node__count_folded_rows(node);
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900228
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300229 list_for_each_entry(chain, &node->val, list) {
230 ++n;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900231 unfolded = chain->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300232 }
233
234 if (unfolded)
235 n += callchain_node__count_rows_rb_tree(node);
236
237 return n;
238}
239
240static int callchain__count_rows(struct rb_root *chain)
241{
242 struct rb_node *nd;
243 int n = 0;
244
245 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
246 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
247 n += callchain_node__count_rows(node);
248 }
249
250 return n;
251}
252
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900253static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
254 bool include_children)
255{
256 int count = 0;
257 struct rb_node *node;
258 struct hist_entry *child;
259
260 if (he->leaf)
261 return callchain__count_rows(&he->sorted_chain);
262
263 node = rb_first(&he->hroot_out);
264 while (node) {
265 float percent;
266
267 child = rb_entry(node, struct hist_entry, rb_node);
268 percent = hist_entry__get_percent_limit(child);
269
270 if (!child->filtered && percent >= hb->min_pcnt) {
271 count++;
272
273 if (include_children && child->unfolded)
274 count += hierarchy_count_rows(hb, child, true);
275 }
276
277 node = rb_next(node);
278 }
279 return count;
280}
281
Namhyung Kim3698dab2015-05-05 23:55:46 +0900282static bool hist_entry__toggle_fold(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300283{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900284 if (!he)
Jiri Olsa8493fe12012-04-04 22:21:31 +0200285 return false;
286
Namhyung Kim3698dab2015-05-05 23:55:46 +0900287 if (!he->has_children)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300288 return false;
289
Namhyung Kim3698dab2015-05-05 23:55:46 +0900290 he->unfolded = !he->unfolded;
291 return true;
292}
293
294static bool callchain_list__toggle_fold(struct callchain_list *cl)
295{
296 if (!cl)
297 return false;
298
299 if (!cl->has_children)
300 return false;
301
302 cl->unfolded = !cl->unfolded;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300303 return true;
304}
305
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300306static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300307{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300308 struct rb_node *nd = rb_first(&node->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300309
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300310 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300311 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
312 struct callchain_list *chain;
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300313 bool first = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300314
315 list_for_each_entry(chain, &child->val, list) {
316 if (first) {
317 first = false;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900318 chain->has_children = chain->list.next != &child->val ||
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300319 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300320 } else
Namhyung Kim3698dab2015-05-05 23:55:46 +0900321 chain->has_children = chain->list.next == &child->val &&
Arnaldo Carvalho de Melo293db472010-08-25 16:05:36 -0300322 !RB_EMPTY_ROOT(&child->rb_root);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300323 }
324
325 callchain_node__init_have_children_rb_tree(child);
326 }
327}
328
Namhyung Kima7444af2014-11-24 17:13:27 +0900329static void callchain_node__init_have_children(struct callchain_node *node,
330 bool has_sibling)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300331{
332 struct callchain_list *chain;
333
Namhyung Kima7444af2014-11-24 17:13:27 +0900334 chain = list_entry(node->val.next, struct callchain_list, list);
Namhyung Kim3698dab2015-05-05 23:55:46 +0900335 chain->has_children = has_sibling;
Namhyung Kima7444af2014-11-24 17:13:27 +0900336
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900337 if (node->val.next != node->val.prev) {
Namhyung Kim82162b52014-08-13 15:02:41 +0900338 chain = list_entry(node->val.prev, struct callchain_list, list);
Namhyung Kim3698dab2015-05-05 23:55:46 +0900339 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
Namhyung Kim82162b52014-08-13 15:02:41 +0900340 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300341
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300342 callchain_node__init_have_children_rb_tree(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300343}
344
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300345static void callchain__init_have_children(struct rb_root *root)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300346{
Namhyung Kima7444af2014-11-24 17:13:27 +0900347 struct rb_node *nd = rb_first(root);
348 bool has_sibling = nd && rb_next(nd);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300349
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300350 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300351 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
Namhyung Kima7444af2014-11-24 17:13:27 +0900352 callchain_node__init_have_children(node, has_sibling);
Namhyung Kim8c430a32015-11-09 14:45:44 +0900353 if (callchain_param.mode == CHAIN_FLAT ||
354 callchain_param.mode == CHAIN_FOLDED)
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900355 callchain_node__make_parent_list(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300356 }
357}
358
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300359static void hist_entry__init_have_children(struct hist_entry *he)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300360{
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900361 if (he->init_have_children)
362 return;
363
364 if (he->leaf) {
Namhyung Kim3698dab2015-05-05 23:55:46 +0900365 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300366 callchain__init_have_children(&he->sorted_chain);
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900367 } else {
368 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300369 }
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900370
371 he->init_have_children = true;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300372}
373
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300374static bool hist_browser__toggle_fold(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300375{
Namhyung Kim3698dab2015-05-05 23:55:46 +0900376 struct hist_entry *he = browser->he_selection;
377 struct map_symbol *ms = browser->selection;
378 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
379 bool has_children;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300380
Wang Nan4938cf02015-12-07 02:35:44 +0000381 if (!he || !ms)
382 return false;
383
Namhyung Kim3698dab2015-05-05 23:55:46 +0900384 if (ms == &he->ms)
385 has_children = hist_entry__toggle_fold(he);
386 else
387 has_children = callchain_list__toggle_fold(cl);
388
389 if (has_children) {
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900390 int child_rows = 0;
391
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300392 hist_entry__init_have_children(he);
Namhyung Kimc3b78952014-04-22 15:56:17 +0900393 browser->b.nr_entries -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300394
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900395 if (he->leaf)
396 browser->nr_callchain_rows -= he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300397 else
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900398 browser->nr_hierarchy_entries -= he->nr_rows;
399
400 if (symbol_conf.report_hierarchy)
401 child_rows = hierarchy_count_rows(browser, he, true);
402
403 if (he->unfolded) {
404 if (he->leaf)
405 he->nr_rows = callchain__count_rows(&he->sorted_chain);
406 else
407 he->nr_rows = hierarchy_count_rows(browser, he, false);
408
409 /* account grand children */
410 if (symbol_conf.report_hierarchy)
411 browser->b.nr_entries += child_rows - he->nr_rows;
412 } else {
413 if (symbol_conf.report_hierarchy)
414 browser->b.nr_entries -= child_rows - he->nr_rows;
415
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300416 he->nr_rows = 0;
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900417 }
Namhyung Kimc3b78952014-04-22 15:56:17 +0900418
419 browser->b.nr_entries += he->nr_rows;
Namhyung Kimf5b763f2016-02-25 00:13:43 +0900420
421 if (he->leaf)
422 browser->nr_callchain_rows += he->nr_rows;
423 else
424 browser->nr_hierarchy_entries += he->nr_rows;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300425
426 return true;
427 }
428
429 /* If it doesn't have children, no toggling performed */
430 return false;
431}
432
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300433static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300434{
435 int n = 0;
436 struct rb_node *nd;
437
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300438 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300439 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
440 struct callchain_list *chain;
441 bool has_children = false;
442
443 list_for_each_entry(chain, &child->val, list) {
444 ++n;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900445 callchain_list__set_folding(chain, unfold);
446 has_children = chain->has_children;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300447 }
448
449 if (has_children)
450 n += callchain_node__set_folding_rb_tree(child, unfold);
451 }
452
453 return n;
454}
455
456static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
457{
458 struct callchain_list *chain;
459 bool has_children = false;
460 int n = 0;
461
462 list_for_each_entry(chain, &node->val, list) {
463 ++n;
Namhyung Kim3698dab2015-05-05 23:55:46 +0900464 callchain_list__set_folding(chain, unfold);
465 has_children = chain->has_children;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300466 }
467
468 if (has_children)
469 n += callchain_node__set_folding_rb_tree(node, unfold);
470
471 return n;
472}
473
474static int callchain__set_folding(struct rb_root *chain, bool unfold)
475{
476 struct rb_node *nd;
477 int n = 0;
478
479 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
480 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
481 n += callchain_node__set_folding(node, unfold);
482 }
483
484 return n;
485}
486
Namhyung Kim492b1012016-02-25 00:13:44 +0900487static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
488 bool unfold __maybe_unused)
489{
490 float percent;
491 struct rb_node *nd;
492 struct hist_entry *child;
493 int n = 0;
494
495 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
496 child = rb_entry(nd, struct hist_entry, rb_node);
497 percent = hist_entry__get_percent_limit(child);
498 if (!child->filtered && percent >= hb->min_pcnt)
499 n++;
500 }
501
502 return n;
503}
504
505static void hist_entry__set_folding(struct hist_entry *he,
506 struct hist_browser *hb, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300507{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300508 hist_entry__init_have_children(he);
Namhyung Kim3698dab2015-05-05 23:55:46 +0900509 he->unfolded = unfold ? he->has_children : false;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300510
Namhyung Kim3698dab2015-05-05 23:55:46 +0900511 if (he->has_children) {
Namhyung Kim492b1012016-02-25 00:13:44 +0900512 int n;
513
514 if (he->leaf)
515 n = callchain__set_folding(&he->sorted_chain, unfold);
516 else
517 n = hierarchy_set_folding(hb, he, unfold);
518
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300519 he->nr_rows = unfold ? n : 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300520 } else
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300521 he->nr_rows = 0;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300522}
523
Namhyung Kimc3b78952014-04-22 15:56:17 +0900524static void
525__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300526{
527 struct rb_node *nd;
Namhyung Kim492b1012016-02-25 00:13:44 +0900528 struct hist_entry *he;
529 double percent;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300530
Namhyung Kim492b1012016-02-25 00:13:44 +0900531 nd = rb_first(&browser->hists->entries);
532 while (nd) {
533 he = rb_entry(nd, struct hist_entry, rb_node);
534
535 /* set folding state even if it's currently folded */
536 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
537
538 hist_entry__set_folding(he, browser, unfold);
539
540 percent = hist_entry__get_percent_limit(he);
541 if (he->filtered || percent < browser->min_pcnt)
542 continue;
543
544 if (!he->depth || unfold)
545 browser->nr_hierarchy_entries++;
546 if (he->leaf)
547 browser->nr_callchain_rows += he->nr_rows;
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300548 }
549}
550
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300551static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300552{
Namhyung Kim492b1012016-02-25 00:13:44 +0900553 browser->nr_hierarchy_entries = 0;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900554 browser->nr_callchain_rows = 0;
555 __hist_browser__set_folding(browser, unfold);
556
557 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300558 /* Go to the start, we may be way after valid entries after a collapse */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300559 ui_browser__reset_index(&browser->b);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300560}
561
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200562static void ui_browser__warn_lost_events(struct ui_browser *browser)
563{
564 ui_browser__warning(browser, 4,
565 "Events are being lost, check IO/CPU overload!\n\n"
566 "You may want to run 'perf' using a RT scheduler policy:\n\n"
567 " perf top -r 80\n\n"
568 "Or reduce the sampling frequency.");
569}
570
Arnaldo Carvalho de Melo5f00b0f2015-06-19 17:30:20 -0300571static int hist_browser__run(struct hist_browser *browser, const char *help)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300572{
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300573 int key;
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300574 char title[160];
Namhyung Kimc2a51ab2015-04-22 16:18:15 +0900575 struct hist_browser_timer *hbt = browser->hbt;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900576 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300577
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300578 browser->b.entries = &browser->hists->entries;
Namhyung Kimc3b78952014-04-22 15:56:17 +0900579 browser->b.nr_entries = hist_browser__nr_entries(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300580
Taeung Song1e378eb2014-10-07 16:13:15 +0900581 hists__browser_title(browser->hists, hbt, title, sizeof(title));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300582
Namhyung Kim090cff32016-01-11 19:53:14 +0900583 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300584 return -1;
585
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300586 while (1) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300587 key = ui_browser__run(&browser->b, delay_secs);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300588
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300589 switch (key) {
Namhyung Kimfa5df942013-05-14 11:09:05 +0900590 case K_TIMER: {
591 u64 nr_entries;
Namhyung Kim9783adf2012-11-02 14:50:05 +0900592 hbt->timer(hbt->arg);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900593
Namhyung Kimc3b78952014-04-22 15:56:17 +0900594 if (hist_browser__has_filter(browser))
Namhyung Kim112f7612014-04-22 14:05:35 +0900595 hist_browser__update_nr_entries(browser);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900596
Namhyung Kimc3b78952014-04-22 15:56:17 +0900597 nr_entries = hist_browser__nr_entries(browser);
Namhyung Kimfa5df942013-05-14 11:09:05 +0900598 ui_browser__update_nr_entries(&browser->b, nr_entries);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200599
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300600 if (browser->hists->stats.nr_lost_warned !=
601 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
602 browser->hists->stats.nr_lost_warned =
603 browser->hists->stats.nr_events[PERF_RECORD_LOST];
604 ui_browser__warn_lost_events(&browser->b);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -0200605 }
606
Taeung Song1e378eb2014-10-07 16:13:15 +0900607 hists__browser_title(browser->hists,
608 hbt, title, sizeof(title));
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300609 ui_browser__show_title(&browser->b, title);
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -0300610 continue;
Namhyung Kimfa5df942013-05-14 11:09:05 +0900611 }
Arnaldo Carvalho de Melo46941532010-08-10 15:50:07 -0300612 case 'D': { /* Debug */
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300613 static int seq;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300614 struct hist_entry *h = rb_entry(browser->b.top,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300615 struct hist_entry, rb_node);
616 ui_helpline__pop();
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -0300617 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 -0300618 seq++, browser->b.nr_entries,
619 browser->hists->nr_entries,
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -0300620 browser->b.rows,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300621 browser->b.index,
622 browser->b.top_idx,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300623 h->row_offset, h->nr_rows);
624 }
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300625 break;
626 case 'C':
627 /* Collapse the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300628 hist_browser__set_folding(browser, false);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300629 break;
630 case 'E':
631 /* Expand the whole world. */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300632 hist_browser__set_folding(browser, true);
Arnaldo Carvalho de Melo3c916cc2010-08-25 17:18:35 -0300633 break;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +0200634 case 'H':
635 browser->show_headers = !browser->show_headers;
636 hist_browser__update_rows(browser);
637 break;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -0200638 case K_ENTER:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300639 if (hist_browser__toggle_fold(browser))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300640 break;
641 /* fall thru */
642 default:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300643 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300644 }
645 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300646out:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -0300647 ui_browser__hide(&browser->b);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -0300648 return key;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300649}
650
Namhyung Kim39ee5332014-08-22 09:13:21 +0900651struct callchain_print_arg {
652 /* for hists browser */
653 off_t row_offset;
654 bool is_current_entry;
655
656 /* for file dump */
657 FILE *fp;
658 int printed;
659};
660
661typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
662 struct callchain_list *chain,
663 const char *str, int offset,
664 unsigned short row,
665 struct callchain_print_arg *arg);
666
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900667static void hist_browser__show_callchain_entry(struct hist_browser *browser,
668 struct callchain_list *chain,
Namhyung Kim39ee5332014-08-22 09:13:21 +0900669 const char *str, int offset,
670 unsigned short row,
671 struct callchain_print_arg *arg)
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900672{
673 int color, width;
Namhyung Kim39ee5332014-08-22 09:13:21 +0900674 char folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Melo70e97272015-03-19 16:07:21 -0300675 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900676
677 color = HE_COLORSET_NORMAL;
678 width = browser->b.width - (offset + 2);
679 if (ui_browser__is_current_entry(&browser->b, row)) {
680 browser->selection = &chain->ms;
681 color = HE_COLORSET_SELECTED;
Namhyung Kim39ee5332014-08-22 09:13:21 +0900682 arg->is_current_entry = true;
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900683 }
684
685 ui_browser__set_color(&browser->b, color);
686 hist_browser__gotorc(browser, row, 0);
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -0300687 ui_browser__write_nstring(&browser->b, " ", offset);
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -0300688 ui_browser__printf(&browser->b, "%c", folded_sign);
Arnaldo Carvalho de Melo70e97272015-03-19 16:07:21 -0300689 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -0300690 ui_browser__write_nstring(&browser->b, str, width);
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900691}
692
Namhyung Kim39ee5332014-08-22 09:13:21 +0900693static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
694 struct callchain_list *chain,
695 const char *str, int offset,
696 unsigned short row __maybe_unused,
697 struct callchain_print_arg *arg)
698{
699 char folded_sign = callchain_list__folded(chain);
700
701 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
702 folded_sign, str);
703}
704
705typedef bool (*check_output_full_fn)(struct hist_browser *browser,
706 unsigned short row);
707
708static bool hist_browser__check_output_full(struct hist_browser *browser,
709 unsigned short row)
710{
711 return browser->b.rows == row;
712}
713
714static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
715 unsigned short row __maybe_unused)
716{
717 return false;
718}
719
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300720#define LEVEL_OFFSET_STEP 3
721
Namhyung Kim18bb8382015-11-09 14:45:42 +0900722static int hist_browser__show_callchain_list(struct hist_browser *browser,
723 struct callchain_node *node,
724 struct callchain_list *chain,
725 unsigned short row, u64 total,
726 bool need_percent, int offset,
727 print_callchain_entry_fn print,
728 struct callchain_print_arg *arg)
729{
730 char bf[1024], *alloc_str;
731 const char *str;
732
733 if (arg->row_offset != 0) {
734 arg->row_offset--;
735 return 0;
736 }
737
738 alloc_str = NULL;
739 str = callchain_list__sym_name(chain, bf, sizeof(bf),
740 browser->show_dso);
741
742 if (need_percent) {
743 char buf[64];
744
745 callchain_node__scnprintf_value(node, buf, sizeof(buf),
746 total);
747
748 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
749 str = "Not enough memory!";
750 else
751 str = alloc_str;
752 }
753
754 print(browser, chain, str, offset, row, arg);
755
756 free(alloc_str);
757 return 1;
758}
759
Namhyung Kim59c624e2016-01-28 00:40:56 +0900760static bool check_percent_display(struct rb_node *node, u64 parent_total)
761{
762 struct callchain_node *child;
763
764 if (node == NULL)
765 return false;
766
767 if (rb_next(node))
768 return true;
769
770 child = rb_entry(node, struct callchain_node, rb_node);
771 return callchain_cumul_hits(child) != parent_total;
772}
773
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900774static int hist_browser__show_callchain_flat(struct hist_browser *browser,
775 struct rb_root *root,
776 unsigned short row, u64 total,
Namhyung Kim59c624e2016-01-28 00:40:56 +0900777 u64 parent_total,
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900778 print_callchain_entry_fn print,
779 struct callchain_print_arg *arg,
780 check_output_full_fn is_output_full)
781{
782 struct rb_node *node;
783 int first_row = row, offset = LEVEL_OFFSET_STEP;
784 bool need_percent;
785
786 node = rb_first(root);
Namhyung Kim59c624e2016-01-28 00:40:56 +0900787 need_percent = check_percent_display(node, parent_total);
Namhyung Kim4b3a3212015-11-09 14:45:43 +0900788
789 while (node) {
790 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
791 struct rb_node *next = rb_next(node);
792 struct callchain_list *chain;
793 char folded_sign = ' ';
794 int first = true;
795 int extra_offset = 0;
796
797 list_for_each_entry(chain, &child->parent_val, list) {
798 bool was_first = first;
799
800 if (first)
801 first = false;
802 else if (need_percent)
803 extra_offset = LEVEL_OFFSET_STEP;
804
805 folded_sign = callchain_list__folded(chain);
806
807 row += hist_browser__show_callchain_list(browser, child,
808 chain, row, total,
809 was_first && need_percent,
810 offset + extra_offset,
811 print, arg);
812
813 if (is_output_full(browser, row))
814 goto out;
815
816 if (folded_sign == '+')
817 goto next;
818 }
819
820 list_for_each_entry(chain, &child->val, list) {
821 bool was_first = first;
822
823 if (first)
824 first = false;
825 else if (need_percent)
826 extra_offset = LEVEL_OFFSET_STEP;
827
828 folded_sign = callchain_list__folded(chain);
829
830 row += hist_browser__show_callchain_list(browser, child,
831 chain, row, total,
832 was_first && need_percent,
833 offset + extra_offset,
834 print, arg);
835
836 if (is_output_full(browser, row))
837 goto out;
838
839 if (folded_sign == '+')
840 break;
841 }
842
843next:
844 if (is_output_full(browser, row))
845 break;
846 node = next;
847 }
848out:
849 return row - first_row;
850}
851
Namhyung Kim8c430a32015-11-09 14:45:44 +0900852static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
853 struct callchain_list *chain,
854 char *value_str, char *old_str)
855{
856 char bf[1024];
857 const char *str;
858 char *new;
859
860 str = callchain_list__sym_name(chain, bf, sizeof(bf),
861 browser->show_dso);
862 if (old_str) {
863 if (asprintf(&new, "%s%s%s", old_str,
864 symbol_conf.field_sep ?: ";", str) < 0)
865 new = NULL;
866 } else {
867 if (value_str) {
868 if (asprintf(&new, "%s %s", value_str, str) < 0)
869 new = NULL;
870 } else {
871 if (asprintf(&new, "%s", str) < 0)
872 new = NULL;
873 }
874 }
875 return new;
876}
877
878static int hist_browser__show_callchain_folded(struct hist_browser *browser,
879 struct rb_root *root,
880 unsigned short row, u64 total,
Namhyung Kim59c624e2016-01-28 00:40:56 +0900881 u64 parent_total,
Namhyung Kim8c430a32015-11-09 14:45:44 +0900882 print_callchain_entry_fn print,
883 struct callchain_print_arg *arg,
884 check_output_full_fn is_output_full)
885{
886 struct rb_node *node;
887 int first_row = row, offset = LEVEL_OFFSET_STEP;
888 bool need_percent;
889
890 node = rb_first(root);
Namhyung Kim59c624e2016-01-28 00:40:56 +0900891 need_percent = check_percent_display(node, parent_total);
Namhyung Kim8c430a32015-11-09 14:45:44 +0900892
893 while (node) {
894 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
895 struct rb_node *next = rb_next(node);
896 struct callchain_list *chain, *first_chain = NULL;
897 int first = true;
898 char *value_str = NULL, *value_str_alloc = NULL;
899 char *chain_str = NULL, *chain_str_alloc = NULL;
900
901 if (arg->row_offset != 0) {
902 arg->row_offset--;
903 goto next;
904 }
905
906 if (need_percent) {
907 char buf[64];
908
909 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
910 if (asprintf(&value_str, "%s", buf) < 0) {
911 value_str = (char *)"<...>";
912 goto do_print;
913 }
914 value_str_alloc = value_str;
915 }
916
917 list_for_each_entry(chain, &child->parent_val, list) {
918 chain_str = hist_browser__folded_callchain_str(browser,
919 chain, value_str, chain_str);
920 if (first) {
921 first = false;
922 first_chain = chain;
923 }
924
925 if (chain_str == NULL) {
926 chain_str = (char *)"Not enough memory!";
927 goto do_print;
928 }
929
930 chain_str_alloc = chain_str;
931 }
932
933 list_for_each_entry(chain, &child->val, list) {
934 chain_str = hist_browser__folded_callchain_str(browser,
935 chain, value_str, chain_str);
936 if (first) {
937 first = false;
938 first_chain = chain;
939 }
940
941 if (chain_str == NULL) {
942 chain_str = (char *)"Not enough memory!";
943 goto do_print;
944 }
945
946 chain_str_alloc = chain_str;
947 }
948
949do_print:
950 print(browser, first_chain, chain_str, offset, row++, arg);
951 free(value_str_alloc);
952 free(chain_str_alloc);
953
954next:
955 if (is_output_full(browser, row))
956 break;
957 node = next;
958 }
959
960 return row - first_row;
961}
962
Namhyung Kim0c841c62016-01-28 00:40:54 +0900963static int hist_browser__show_callchain_graph(struct hist_browser *browser,
Namhyung Kimc09a7e72014-08-21 10:15:45 +0900964 struct rb_root *root, int level,
Namhyung Kim39ee5332014-08-22 09:13:21 +0900965 unsigned short row, u64 total,
Namhyung Kim5eca1042016-01-28 00:40:55 +0900966 u64 parent_total,
Namhyung Kim39ee5332014-08-22 09:13:21 +0900967 print_callchain_entry_fn print,
968 struct callchain_print_arg *arg,
969 check_output_full_fn is_output_full)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300970{
971 struct rb_node *node;
Namhyung Kimf4536dd2014-08-20 17:07:57 +0900972 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
Namhyung Kim4087d112014-11-24 17:13:26 +0900973 bool need_percent;
Namhyung Kim5eca1042016-01-28 00:40:55 +0900974 u64 percent_total = total;
975
976 if (callchain_param.mode == CHAIN_GRAPH_REL)
977 percent_total = parent_total;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300978
Namhyung Kimc09a7e72014-08-21 10:15:45 +0900979 node = rb_first(root);
Namhyung Kim59c624e2016-01-28 00:40:56 +0900980 need_percent = check_percent_display(node, parent_total);
Namhyung Kim4087d112014-11-24 17:13:26 +0900981
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300982 while (node) {
983 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
984 struct rb_node *next = rb_next(node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300985 struct callchain_list *chain;
986 char folded_sign = ' ';
987 int first = true;
988 int extra_offset = 0;
989
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300990 list_for_each_entry(chain, &child->val, list) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300991 bool was_first = first;
992
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -0300993 if (first)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300994 first = false;
Namhyung Kim4087d112014-11-24 17:13:26 +0900995 else if (need_percent)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300996 extra_offset = LEVEL_OFFSET_STEP;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300997
998 folded_sign = callchain_list__folded(chain);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -0300999
Namhyung Kim18bb8382015-11-09 14:45:42 +09001000 row += hist_browser__show_callchain_list(browser, child,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001001 chain, row, percent_total,
Namhyung Kim18bb8382015-11-09 14:45:42 +09001002 was_first && need_percent,
1003 offset + extra_offset,
1004 print, arg);
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001005
Namhyung Kim18bb8382015-11-09 14:45:42 +09001006 if (is_output_full(browser, row))
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001007 goto out;
Namhyung Kim18bb8382015-11-09 14:45:42 +09001008
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001009 if (folded_sign == '+')
1010 break;
1011 }
1012
1013 if (folded_sign == '-') {
1014 const int new_level = level + (extra_offset ? 2 : 1);
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001015
Namhyung Kim0c841c62016-01-28 00:40:54 +09001016 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001017 new_level, row, total,
1018 child->children_hit,
Namhyung Kim39ee5332014-08-22 09:13:21 +09001019 print, arg, is_output_full);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001020 }
Namhyung Kim39ee5332014-08-22 09:13:21 +09001021 if (is_output_full(browser, row))
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001022 break;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001023 node = next;
1024 }
1025out:
1026 return row - first_row;
1027}
1028
Namhyung Kim0c841c62016-01-28 00:40:54 +09001029static int hist_browser__show_callchain(struct hist_browser *browser,
1030 struct hist_entry *entry, int level,
1031 unsigned short row,
1032 print_callchain_entry_fn print,
1033 struct callchain_print_arg *arg,
1034 check_output_full_fn is_output_full)
1035{
1036 u64 total = hists__total_period(entry->hists);
Namhyung Kim5eca1042016-01-28 00:40:55 +09001037 u64 parent_total;
Namhyung Kim0c841c62016-01-28 00:40:54 +09001038 int printed;
1039
Namhyung Kim5eca1042016-01-28 00:40:55 +09001040 if (symbol_conf.cumulate_callchain)
1041 parent_total = entry->stat_acc->period;
1042 else
1043 parent_total = entry->stat.period;
Namhyung Kim0c841c62016-01-28 00:40:54 +09001044
1045 if (callchain_param.mode == CHAIN_FLAT) {
1046 printed = hist_browser__show_callchain_flat(browser,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001047 &entry->sorted_chain, row,
1048 total, parent_total, print, arg,
1049 is_output_full);
Namhyung Kim0c841c62016-01-28 00:40:54 +09001050 } else if (callchain_param.mode == CHAIN_FOLDED) {
1051 printed = hist_browser__show_callchain_folded(browser,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001052 &entry->sorted_chain, row,
1053 total, parent_total, print, arg,
1054 is_output_full);
Namhyung Kim0c841c62016-01-28 00:40:54 +09001055 } else {
1056 printed = hist_browser__show_callchain_graph(browser,
Namhyung Kim5eca1042016-01-28 00:40:55 +09001057 &entry->sorted_chain, level, row,
1058 total, parent_total, print, arg,
1059 is_output_full);
Namhyung Kim0c841c62016-01-28 00:40:54 +09001060 }
1061
1062 if (arg->is_current_entry)
1063 browser->he_selection = entry;
1064
1065 return printed;
1066}
1067
Namhyung Kim89701462013-01-22 18:09:38 +09001068struct hpp_arg {
1069 struct ui_browser *b;
1070 char folded_sign;
1071 bool current_entry;
1072};
1073
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001074static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1075{
1076 struct hpp_arg *arg = hpp->ptr;
Namhyung Kimd6751072014-07-31 14:47:36 +09001077 int ret, len;
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001078 va_list args;
1079 double percent;
1080
1081 va_start(args, fmt);
Namhyung Kimd6751072014-07-31 14:47:36 +09001082 len = va_arg(args, int);
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001083 percent = va_arg(args, double);
1084 va_end(args);
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001085
Namhyung Kim89701462013-01-22 18:09:38 +09001086 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001087
Namhyung Kimd6751072014-07-31 14:47:36 +09001088 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001089 ui_browser__printf(arg->b, "%s", hpp->buf);
Namhyung Kim89701462013-01-22 18:09:38 +09001090
Namhyung Kim2f6d9002014-03-03 10:14:05 +09001091 advance_hpp(hpp, ret);
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001092 return ret;
Namhyung Kimf5951d52012-09-03 11:53:09 +09001093}
1094
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001095#define __HPP_COLOR_PERCENT_FN(_type, _field) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001096static u64 __hpp_get_##_field(struct hist_entry *he) \
1097{ \
1098 return he->stat._field; \
1099} \
1100 \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +01001101static int \
Namhyung Kim5b591662014-07-31 14:47:38 +09001102hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
Jiri Olsa2c5d4b42013-01-31 23:31:11 +01001103 struct perf_hpp *hpp, \
1104 struct hist_entry *he) \
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001105{ \
Namhyung Kim5b591662014-07-31 14:47:38 +09001106 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1107 __hpp__slsmg_color_printf, true); \
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001108}
Namhyung Kimf5951d52012-09-03 11:53:09 +09001109
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001110#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1111static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1112{ \
1113 return he->stat_acc->_field; \
1114} \
1115 \
1116static int \
Namhyung Kim5b591662014-07-31 14:47:38 +09001117hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001118 struct perf_hpp *hpp, \
1119 struct hist_entry *he) \
1120{ \
1121 if (!symbol_conf.cumulate_callchain) { \
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001122 struct hpp_arg *arg = hpp->ptr; \
Namhyung Kim5b591662014-07-31 14:47:38 +09001123 int len = fmt->user_len ?: fmt->len; \
Namhyung Kimd6751072014-07-31 14:47:36 +09001124 int ret = scnprintf(hpp->buf, hpp->size, \
Namhyung Kim5b591662014-07-31 14:47:38 +09001125 "%*s", len, "N/A"); \
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001126 ui_browser__printf(arg->b, "%s", hpp->buf); \
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001127 \
1128 return ret; \
1129 } \
Namhyung Kim5b591662014-07-31 14:47:38 +09001130 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1131 " %*.2f%%", __hpp__slsmg_color_printf, true); \
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001132}
1133
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001134__HPP_COLOR_PERCENT_FN(overhead, period)
1135__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1136__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1137__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1138__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001139__HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
Namhyung Kim5aed9d22013-01-22 18:09:35 +09001140
1141#undef __HPP_COLOR_PERCENT_FN
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001142#undef __HPP_COLOR_ACC_PERCENT_FN
Namhyung Kimf5951d52012-09-03 11:53:09 +09001143
1144void hist_browser__init_hpp(void)
1145{
Namhyung Kimf5951d52012-09-03 11:53:09 +09001146 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1147 hist_browser__hpp_color_overhead;
1148 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1149 hist_browser__hpp_color_overhead_sys;
1150 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1151 hist_browser__hpp_color_overhead_us;
1152 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1153 hist_browser__hpp_color_overhead_guest_sys;
1154 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1155 hist_browser__hpp_color_overhead_guest_us;
Namhyung Kim0434ddd2013-10-30 16:12:59 +09001156 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1157 hist_browser__hpp_color_overhead_acc;
Namhyung Kimf5951d52012-09-03 11:53:09 +09001158}
1159
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001160static int hist_browser__show_entry(struct hist_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001161 struct hist_entry *entry,
1162 unsigned short row)
1163{
Jiri Olsa12400052012-10-13 00:06:16 +02001164 int printed = 0;
Namhyung Kim67d25912012-09-12 15:35:06 +09001165 int width = browser->b.width;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001166 char folded_sign = ' ';
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001167 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001168 off_t row_offset = entry->row_offset;
Namhyung Kim63a1a3d2012-10-15 18:14:35 -03001169 bool first = true;
Jiri Olsa12400052012-10-13 00:06:16 +02001170 struct perf_hpp_fmt *fmt;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001171
1172 if (current_entry) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001173 browser->he_selection = entry;
1174 browser->selection = &entry->ms;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001175 }
1176
1177 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo163caed2010-08-25 16:30:03 -03001178 hist_entry__init_have_children(entry);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001179 folded_sign = hist_entry__folded(entry);
1180 }
1181
1182 if (row_offset == 0) {
Namhyung Kim89701462013-01-22 18:09:38 +09001183 struct hpp_arg arg = {
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001184 .b = &browser->b,
Namhyung Kim89701462013-01-22 18:09:38 +09001185 .folded_sign = folded_sign,
1186 .current_entry = current_entry,
1187 };
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001188 int column = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001189
Arnaldo Carvalho de Meloca3ff332014-07-01 16:42:24 -03001190 hist_browser__gotorc(browser, row, 0);
Namhyung Kimf5951d52012-09-03 11:53:09 +09001191
Jiri Olsaf0786af2016-01-18 10:24:23 +01001192 hists__for_each_format(browser->hists, fmt) {
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001193 char s[2048];
1194 struct perf_hpp hpp = {
1195 .buf = s,
1196 .size = sizeof(s),
1197 .ptr = &arg,
1198 };
1199
Namhyung Kim361459f2015-12-23 02:07:08 +09001200 if (perf_hpp__should_skip(fmt, entry->hists) ||
1201 column++ < browser->b.horiz_scroll)
Namhyung Kime67d49a2014-03-18 13:00:59 +09001202 continue;
1203
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001204 if (current_entry && browser->b.navkeypressed) {
1205 ui_browser__set_color(&browser->b,
1206 HE_COLORSET_SELECTED);
1207 } else {
1208 ui_browser__set_color(&browser->b,
1209 HE_COLORSET_NORMAL);
1210 }
1211
1212 if (first) {
1213 if (symbol_conf.use_callchain) {
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001214 ui_browser__printf(&browser->b, "%c ", folded_sign);
Namhyung Kimfb821c9e2014-03-03 17:05:19 +09001215 width -= 2;
1216 }
1217 first = false;
1218 } else {
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001219 ui_browser__printf(&browser->b, " ");
Namhyung Kimf5951d52012-09-03 11:53:09 +09001220 width -= 2;
1221 }
1222
Jiri Olsa12400052012-10-13 00:06:16 +02001223 if (fmt->color) {
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001224 int ret = fmt->color(fmt, &hpp, entry);
1225 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1226 /*
1227 * fmt->color() already used ui_browser to
1228 * print the non alignment bits, skip it (+ret):
1229 */
1230 ui_browser__printf(&browser->b, "%s", s + ret);
Namhyung Kimf5951d52012-09-03 11:53:09 +09001231 } else {
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001232 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03001233 ui_browser__printf(&browser->b, "%s", s);
Namhyung Kimf5951d52012-09-03 11:53:09 +09001234 }
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001235 width -= hpp.buf - s;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001236 }
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -02001237
1238 /* The scroll bar isn't being used */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001239 if (!browser->b.navkeypressed)
Arnaldo Carvalho de Meloc172f742011-10-18 14:31:35 -02001240 width += 1;
1241
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -03001242 ui_browser__write_nstring(&browser->b, "", width);
Namhyung Kim26d8b332014-03-03 16:16:20 +09001243
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001244 ++row;
1245 ++printed;
1246 } else
1247 --row_offset;
1248
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -03001249 if (folded_sign == '-' && row != browser->b.rows) {
Namhyung Kim39ee5332014-08-22 09:13:21 +09001250 struct callchain_print_arg arg = {
1251 .row_offset = row_offset,
1252 .is_current_entry = current_entry,
1253 };
Namhyung Kimc09a7e72014-08-21 10:15:45 +09001254
Namhyung Kim0c841c62016-01-28 00:40:54 +09001255 printed += hist_browser__show_callchain(browser, entry, 1, row,
Namhyung Kim4b3a3212015-11-09 14:45:43 +09001256 hist_browser__show_callchain_entry, &arg,
1257 hist_browser__check_output_full);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001258 }
1259
1260 return printed;
1261}
1262
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001263static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1264 struct hist_entry *entry,
1265 unsigned short row,
1266 int level, int nr_sort_keys)
1267{
1268 int printed = 0;
1269 int width = browser->b.width;
1270 char folded_sign = ' ';
1271 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1272 off_t row_offset = entry->row_offset;
1273 bool first = true;
1274 struct perf_hpp_fmt *fmt;
1275 struct hpp_arg arg = {
1276 .b = &browser->b,
1277 .current_entry = current_entry,
1278 };
1279 int column = 0;
1280 int hierarchy_indent = (nr_sort_keys - 1) * HIERARCHY_INDENT;
1281
1282 if (current_entry) {
1283 browser->he_selection = entry;
1284 browser->selection = &entry->ms;
1285 }
1286
1287 hist_entry__init_have_children(entry);
1288 folded_sign = hist_entry__folded(entry);
1289 arg.folded_sign = folded_sign;
1290
1291 if (entry->leaf && row_offset) {
1292 row_offset--;
1293 goto show_callchain;
1294 }
1295
1296 hist_browser__gotorc(browser, row, 0);
1297
1298 if (current_entry && browser->b.navkeypressed)
1299 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1300 else
1301 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1302
1303 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1304 width -= level * HIERARCHY_INDENT;
1305
1306 hists__for_each_format(entry->hists, fmt) {
1307 char s[2048];
1308 struct perf_hpp hpp = {
1309 .buf = s,
1310 .size = sizeof(s),
1311 .ptr = &arg,
1312 };
1313
1314 if (perf_hpp__should_skip(fmt, entry->hists) ||
1315 column++ < browser->b.horiz_scroll)
1316 continue;
1317
1318 if (perf_hpp__is_sort_entry(fmt) ||
1319 perf_hpp__is_dynamic_entry(fmt))
1320 break;
1321
1322 if (current_entry && browser->b.navkeypressed) {
1323 ui_browser__set_color(&browser->b,
1324 HE_COLORSET_SELECTED);
1325 } else {
1326 ui_browser__set_color(&browser->b,
1327 HE_COLORSET_NORMAL);
1328 }
1329
1330 if (first) {
1331 ui_browser__printf(&browser->b, "%c", folded_sign);
1332 width--;
1333 first = false;
1334 } else {
1335 ui_browser__printf(&browser->b, " ");
1336 width -= 2;
1337 }
1338
1339 if (fmt->color) {
1340 int ret = fmt->color(fmt, &hpp, entry);
1341 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1342 /*
1343 * fmt->color() already used ui_browser to
1344 * print the non alignment bits, skip it (+ret):
1345 */
1346 ui_browser__printf(&browser->b, "%s", s + ret);
1347 } else {
1348 int ret = fmt->entry(fmt, &hpp, entry);
1349 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1350 ui_browser__printf(&browser->b, "%s", s);
1351 }
1352 width -= hpp.buf - s;
1353 }
1354
1355 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1356 width -= hierarchy_indent;
1357
1358 if (column >= browser->b.horiz_scroll) {
1359 char s[2048];
1360 struct perf_hpp hpp = {
1361 .buf = s,
1362 .size = sizeof(s),
1363 .ptr = &arg,
1364 };
1365
1366 if (current_entry && browser->b.navkeypressed) {
1367 ui_browser__set_color(&browser->b,
1368 HE_COLORSET_SELECTED);
1369 } else {
1370 ui_browser__set_color(&browser->b,
1371 HE_COLORSET_NORMAL);
1372 }
1373
1374 ui_browser__write_nstring(&browser->b, "", 2);
1375 width -= 2;
1376
1377 /*
1378 * No need to call hist_entry__snprintf_alignment()
1379 * since this fmt is always the last column in the
1380 * hierarchy mode.
1381 */
1382 fmt = entry->fmt;
1383 if (fmt->color) {
1384 width -= fmt->color(fmt, &hpp, entry);
1385 } else {
1386 width -= fmt->entry(fmt, &hpp, entry);
1387 ui_browser__printf(&browser->b, "%s", s);
1388 }
1389 }
1390
1391 /* The scroll bar isn't being used */
1392 if (!browser->b.navkeypressed)
1393 width += 1;
1394
1395 ui_browser__write_nstring(&browser->b, "", width);
1396
1397 ++row;
1398 ++printed;
1399
1400show_callchain:
1401 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1402 struct callchain_print_arg carg = {
1403 .row_offset = row_offset,
1404 };
1405
1406 printed += hist_browser__show_callchain(browser, entry,
1407 level + 1, row,
1408 hist_browser__show_callchain_entry, &carg,
1409 hist_browser__check_output_full);
1410 }
1411
1412 return printed;
1413}
1414
Jiri Olsa81a888f2014-06-14 15:44:52 +02001415static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1416{
1417 advance_hpp(hpp, inc);
1418 return hpp->size <= 0;
1419}
1420
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001421static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
Jiri Olsa81a888f2014-06-14 15:44:52 +02001422{
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001423 struct hists *hists = browser->hists;
Jiri Olsa81a888f2014-06-14 15:44:52 +02001424 struct perf_hpp dummy_hpp = {
1425 .buf = buf,
1426 .size = size,
1427 };
1428 struct perf_hpp_fmt *fmt;
1429 size_t ret = 0;
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001430 int column = 0;
Jiri Olsa81a888f2014-06-14 15:44:52 +02001431
1432 if (symbol_conf.use_callchain) {
1433 ret = scnprintf(buf, size, " ");
1434 if (advance_hpp_check(&dummy_hpp, ret))
1435 return ret;
1436 }
1437
Jiri Olsaf0786af2016-01-18 10:24:23 +01001438 hists__for_each_format(browser->hists, fmt) {
Namhyung Kim361459f2015-12-23 02:07:08 +09001439 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
Jiri Olsa81a888f2014-06-14 15:44:52 +02001440 continue;
1441
Jiri Olsa81a888f2014-06-14 15:44:52 +02001442 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1443 if (advance_hpp_check(&dummy_hpp, ret))
1444 break;
1445
1446 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1447 if (advance_hpp_check(&dummy_hpp, ret))
1448 break;
1449 }
1450
1451 return ret;
1452}
1453
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001454static void hist_browser__show_headers(struct hist_browser *browser)
1455{
Jiri Olsa81a888f2014-06-14 15:44:52 +02001456 char headers[1024];
1457
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03001458 hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001459 ui_browser__gotorc(&browser->b, 0, 0);
1460 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -03001461 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001462}
1463
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -03001464static void ui_browser__hists_init_top(struct ui_browser *browser)
1465{
1466 if (browser->top == NULL) {
1467 struct hist_browser *hb;
1468
1469 hb = container_of(browser, struct hist_browser, b);
1470 browser->top = rb_first(&hb->hists->entries);
1471 }
1472}
1473
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001474static unsigned int hist_browser__refresh(struct ui_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001475{
1476 unsigned row = 0;
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001477 u16 header_offset = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001478 struct rb_node *nd;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001479 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001480 int nr_sort = hb->hists->hpp_list->nr_sort_keys;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001481
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001482 if (hb->show_headers) {
1483 hist_browser__show_headers(hb);
1484 header_offset = 1;
1485 }
1486
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001487 ui_browser__hists_init_top(browser);
Wang Nan979d2ca2015-12-07 02:35:46 +00001488 hb->he_selection = NULL;
1489 hb->selection = NULL;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001490
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001491 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001492 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +09001493 float percent;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001494
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001495 if (h->filtered) {
1496 /* let it move to sibling */
1497 h->unfolded = false;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001498 continue;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001499 }
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001500
Namhyung Kim14135662013-10-31 10:17:39 +09001501 percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +09001502 if (percent < hb->min_pcnt)
1503 continue;
1504
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001505 if (symbol_conf.report_hierarchy) {
1506 row += hist_browser__show_hierarchy_entry(hb, h, row,
1507 h->depth,
1508 nr_sort);
1509 } else {
1510 row += hist_browser__show_entry(hb, h, row);
1511 }
1512
Arnaldo Carvalho de Melo62c95ae2014-07-01 11:07:54 -03001513 if (row == browser->rows)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001514 break;
1515 }
1516
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02001517 return row + header_offset;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001518}
1519
Namhyung Kim064f1982013-05-14 11:09:04 +09001520static struct rb_node *hists__filter_entries(struct rb_node *nd,
Namhyung Kim064f1982013-05-14 11:09:04 +09001521 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001522{
1523 while (nd != NULL) {
1524 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +09001525 float percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +09001526
Namhyung Kimc0f15272014-04-16 11:16:33 +09001527 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001528 return nd;
1529
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001530 /*
1531 * If it's filtered, its all children also were filtered.
1532 * So move to sibling node.
1533 */
1534 if (rb_next(nd))
1535 nd = rb_next(nd);
1536 else
1537 nd = rb_hierarchy_next(nd);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001538 }
1539
1540 return NULL;
1541}
1542
Namhyung Kim064f1982013-05-14 11:09:04 +09001543static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
Namhyung Kim064f1982013-05-14 11:09:04 +09001544 float min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001545{
1546 while (nd != NULL) {
1547 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kim14135662013-10-31 10:17:39 +09001548 float percent = hist_entry__get_percent_limit(h);
Namhyung Kim064f1982013-05-14 11:09:04 +09001549
1550 if (!h->filtered && percent >= min_pcnt)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001551 return nd;
1552
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001553 nd = rb_hierarchy_prev(nd);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001554 }
1555
1556 return NULL;
1557}
1558
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001559static void ui_browser__hists_seek(struct ui_browser *browser,
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001560 off_t offset, int whence)
1561{
1562 struct hist_entry *h;
1563 struct rb_node *nd;
1564 bool first = true;
Namhyung Kim064f1982013-05-14 11:09:04 +09001565 struct hist_browser *hb;
1566
1567 hb = container_of(browser, struct hist_browser, b);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001568
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001569 if (browser->nr_entries == 0)
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03001570 return;
1571
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001572 ui_browser__hists_init_top(browser);
Arnaldo Carvalho de Melo437cfe72011-10-14 09:31:53 -03001573
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001574 switch (whence) {
1575 case SEEK_SET:
Namhyung Kim064f1982013-05-14 11:09:04 +09001576 nd = hists__filter_entries(rb_first(browser->entries),
Namhyung Kim14135662013-10-31 10:17:39 +09001577 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001578 break;
1579 case SEEK_CUR:
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001580 nd = browser->top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001581 goto do_offset;
1582 case SEEK_END:
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001583 nd = rb_hierarchy_last(rb_last(browser->entries));
1584 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001585 first = false;
1586 break;
1587 default:
1588 return;
1589 }
1590
1591 /*
1592 * Moves not relative to the first visible entry invalidates its
1593 * row_offset:
1594 */
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001595 h = rb_entry(browser->top, struct hist_entry, rb_node);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001596 h->row_offset = 0;
1597
1598 /*
1599 * Here we have to check if nd is expanded (+), if it is we can't go
1600 * the next top level hist_entry, instead we must compute an offset of
1601 * what _not_ to show and not change the first visible entry.
1602 *
1603 * This offset increments when we are going from top to bottom and
1604 * decreases when we're going from bottom to top.
1605 *
1606 * As we don't have backpointers to the top level in the callchains
1607 * structure, we need to always print the whole hist_entry callchain,
1608 * skipping the first ones that are before the first visible entry
1609 * and stop when we printed enough lines to fill the screen.
1610 */
1611do_offset:
Wang Nan837eeb72015-12-07 02:35:45 +00001612 if (!nd)
1613 return;
1614
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001615 if (offset > 0) {
1616 do {
1617 h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001618 if (h->unfolded && h->leaf) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001619 u16 remaining = h->nr_rows - h->row_offset;
1620 if (offset > remaining) {
1621 offset -= remaining;
1622 h->row_offset = 0;
1623 } else {
1624 h->row_offset += offset;
1625 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001626 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001627 break;
1628 }
1629 }
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001630 nd = hists__filter_entries(rb_hierarchy_next(nd),
1631 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001632 if (nd == NULL)
1633 break;
1634 --offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001635 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001636 } while (offset != 0);
1637 } else if (offset < 0) {
1638 while (1) {
1639 h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001640 if (h->unfolded && h->leaf) {
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001641 if (first) {
1642 if (-offset > h->row_offset) {
1643 offset += h->row_offset;
1644 h->row_offset = 0;
1645 } else {
1646 h->row_offset += offset;
1647 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001648 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001649 break;
1650 }
1651 } else {
1652 if (-offset > h->nr_rows) {
1653 offset += h->nr_rows;
1654 h->row_offset = 0;
1655 } else {
1656 h->row_offset = h->nr_rows + offset;
1657 offset = 0;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001658 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001659 break;
1660 }
1661 }
1662 }
1663
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001664 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
Namhyung Kim064f1982013-05-14 11:09:04 +09001665 hb->min_pcnt);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001666 if (nd == NULL)
1667 break;
1668 ++offset;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001669 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001670 if (offset == 0) {
1671 /*
1672 * Last unfiltered hist_entry, check if it is
1673 * unfolded, if it is then we should have
1674 * row_offset at its last entry.
1675 */
1676 h = rb_entry(nd, struct hist_entry, rb_node);
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001677 if (h->unfolded && h->leaf)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001678 h->row_offset = h->nr_rows;
1679 break;
1680 }
1681 first = false;
1682 }
1683 } else {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001684 browser->top = nd;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001685 h = rb_entry(nd, struct hist_entry, rb_node);
1686 h->row_offset = 0;
1687 }
1688}
1689
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001690static int hist_browser__fprintf_callchain(struct hist_browser *browser,
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001691 struct hist_entry *he, FILE *fp,
1692 int level)
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001693{
Namhyung Kim39ee5332014-08-22 09:13:21 +09001694 struct callchain_print_arg arg = {
1695 .fp = fp,
1696 };
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001697
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001698 hist_browser__show_callchain(browser, he, level, 0,
Namhyung Kim39ee5332014-08-22 09:13:21 +09001699 hist_browser__fprintf_callchain_entry, &arg,
1700 hist_browser__check_dump_full);
1701 return arg.printed;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001702}
1703
1704static int hist_browser__fprintf_entry(struct hist_browser *browser,
1705 struct hist_entry *he, FILE *fp)
1706{
1707 char s[8192];
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001708 int printed = 0;
1709 char folded_sign = ' ';
Namhyung Kim26d8b332014-03-03 16:16:20 +09001710 struct perf_hpp hpp = {
1711 .buf = s,
1712 .size = sizeof(s),
1713 };
1714 struct perf_hpp_fmt *fmt;
1715 bool first = true;
1716 int ret;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001717
1718 if (symbol_conf.use_callchain)
1719 folded_sign = hist_entry__folded(he);
1720
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001721 if (symbol_conf.use_callchain)
1722 printed += fprintf(fp, "%c ", folded_sign);
1723
Jiri Olsaf0786af2016-01-18 10:24:23 +01001724 hists__for_each_format(browser->hists, fmt) {
Namhyung Kim361459f2015-12-23 02:07:08 +09001725 if (perf_hpp__should_skip(fmt, he->hists))
Namhyung Kime67d49a2014-03-18 13:00:59 +09001726 continue;
1727
Namhyung Kim26d8b332014-03-03 16:16:20 +09001728 if (!first) {
1729 ret = scnprintf(hpp.buf, hpp.size, " ");
1730 advance_hpp(&hpp, ret);
1731 } else
1732 first = false;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001733
Namhyung Kim26d8b332014-03-03 16:16:20 +09001734 ret = fmt->entry(fmt, &hpp, he);
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001735 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
Namhyung Kim26d8b332014-03-03 16:16:20 +09001736 advance_hpp(&hpp, ret);
1737 }
Arnaldo Carvalho de Melo89fee702016-02-11 17:14:13 -03001738 printed += fprintf(fp, "%s\n", s);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001739
1740 if (folded_sign == '-')
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001741 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1742
1743 return printed;
1744}
1745
1746
1747static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1748 struct hist_entry *he,
1749 FILE *fp, int level,
1750 int nr_sort_keys)
1751{
1752 char s[8192];
1753 int printed = 0;
1754 char folded_sign = ' ';
1755 struct perf_hpp hpp = {
1756 .buf = s,
1757 .size = sizeof(s),
1758 };
1759 struct perf_hpp_fmt *fmt;
1760 bool first = true;
1761 int ret;
1762 int hierarchy_indent = (nr_sort_keys + 1) * HIERARCHY_INDENT;
1763
1764 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1765
1766 folded_sign = hist_entry__folded(he);
1767 printed += fprintf(fp, "%c", folded_sign);
1768
1769 hists__for_each_format(he->hists, fmt) {
1770 if (perf_hpp__should_skip(fmt, he->hists))
1771 continue;
1772
1773 if (perf_hpp__is_sort_entry(fmt) ||
1774 perf_hpp__is_dynamic_entry(fmt))
1775 break;
1776
1777 if (!first) {
1778 ret = scnprintf(hpp.buf, hpp.size, " ");
1779 advance_hpp(&hpp, ret);
1780 } else
1781 first = false;
1782
1783 ret = fmt->entry(fmt, &hpp, he);
1784 advance_hpp(&hpp, ret);
1785 }
1786
1787 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1788 advance_hpp(&hpp, ret);
1789
1790 fmt = he->fmt;
1791 ret = fmt->entry(fmt, &hpp, he);
1792 advance_hpp(&hpp, ret);
1793
1794 printed += fprintf(fp, "%s\n", rtrim(s));
1795
1796 if (he->leaf && folded_sign == '-') {
1797 printed += hist_browser__fprintf_callchain(browser, he, fp,
1798 he->depth + 1);
1799 }
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001800
1801 return printed;
1802}
1803
1804static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1805{
Namhyung Kim064f1982013-05-14 11:09:04 +09001806 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
Namhyung Kim064f1982013-05-14 11:09:04 +09001807 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001808 int printed = 0;
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001809 int nr_sort = browser->hists->hpp_list->nr_sort_keys;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001810
1811 while (nd) {
1812 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1813
Namhyung Kimd0506ed2016-02-25 00:13:45 +09001814 if (symbol_conf.report_hierarchy) {
1815 printed += hist_browser__fprintf_hierarchy_entry(browser,
1816 h, fp,
1817 h->depth,
1818 nr_sort);
1819 } else {
1820 printed += hist_browser__fprintf_entry(browser, h, fp);
1821 }
1822
1823 nd = hists__filter_entries(rb_hierarchy_next(nd),
1824 browser->min_pcnt);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001825 }
1826
1827 return printed;
1828}
1829
1830static int hist_browser__dump(struct hist_browser *browser)
1831{
1832 char filename[64];
1833 FILE *fp;
1834
1835 while (1) {
1836 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1837 if (access(filename, F_OK))
1838 break;
1839 /*
1840 * XXX: Just an arbitrary lazy upper limit
1841 */
1842 if (++browser->print_seq == 8192) {
1843 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1844 return -1;
1845 }
1846 }
1847
1848 fp = fopen(filename, "w");
1849 if (fp == NULL) {
1850 char bf[64];
Kirill A. Shutemov4cc49d42012-07-24 00:06:54 +03001851 const char *err = strerror_r(errno, bf, sizeof(bf));
1852 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03001853 return -1;
1854 }
1855
1856 ++browser->print_seq;
1857 hist_browser__fprintf(browser, fp);
1858 fclose(fp);
1859 ui_helpline__fpush("%s written!", filename);
1860
1861 return 0;
1862}
1863
Namhyung Kimc2a51ab2015-04-22 16:18:15 +09001864static struct hist_browser *hist_browser__new(struct hists *hists,
Namhyung Kimb1a9cee2015-04-22 16:18:17 +09001865 struct hist_browser_timer *hbt,
Kan Liangce80d3b2015-08-28 05:48:04 -04001866 struct perf_env *env)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001867{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001868 struct hist_browser *browser = zalloc(sizeof(*browser));
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001869
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001870 if (browser) {
1871 browser->hists = hists;
1872 browser->b.refresh = hist_browser__refresh;
Arnaldo Carvalho de Melo357cfff2014-07-01 17:01:01 -03001873 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001874 browser->b.seek = ui_browser__hists_seek;
1875 browser->b.use_navkeypressed = true;
Jiri Olsac8302362014-06-27 18:26:58 +02001876 browser->show_headers = symbol_conf.show_hist_headers;
Namhyung Kimc2a51ab2015-04-22 16:18:15 +09001877 browser->hbt = hbt;
Namhyung Kimb1a9cee2015-04-22 16:18:17 +09001878 browser->env = env;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001879 }
1880
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001881 return browser;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001882}
1883
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001884static void hist_browser__delete(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001885{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001886 free(browser);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001887}
1888
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001889static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001890{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001891 return browser->he_selection;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001892}
1893
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001894static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001895{
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001896 return browser->he_selection->thread;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001897}
1898
Taeung Song1e378eb2014-10-07 16:13:15 +09001899/* Check whether the browser is for 'top' or 'report' */
1900static inline bool is_report_browser(void *timer)
1901{
1902 return timer == NULL;
1903}
1904
1905static int hists__browser_title(struct hists *hists,
1906 struct hist_browser_timer *hbt,
1907 char *bf, size_t size)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001908{
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001909 char unit;
1910 int printed;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001911 const struct dso *dso = hists->dso_filter;
1912 const struct thread *thread = hists->thread_filter;
Kan Liang84734b02015-09-04 10:45:45 -04001913 int socket_id = hists->socket_filter;
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001914 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1915 u64 nr_events = hists->stats.total_period;
Namhyung Kim717e2632013-01-22 18:09:44 +09001916 struct perf_evsel *evsel = hists_to_evsel(hists);
Jiri Olsadd00d482014-06-19 13:41:13 +02001917 const char *ev_name = perf_evsel__name(evsel);
Namhyung Kim717e2632013-01-22 18:09:44 +09001918 char buf[512];
1919 size_t buflen = sizeof(buf);
Kan Liang9e207dd2015-08-11 06:30:49 -04001920 char ref[30] = " show reference callgraph, ";
1921 bool enable_ref = false;
Namhyung Kim717e2632013-01-22 18:09:44 +09001922
Namhyung Kimf2148332014-01-14 11:52:48 +09001923 if (symbol_conf.filter_relative) {
1924 nr_samples = hists->stats.nr_non_filtered_samples;
1925 nr_events = hists->stats.total_non_filtered_period;
1926 }
1927
Namhyung Kim759ff492013-03-05 14:53:26 +09001928 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09001929 struct perf_evsel *pos;
1930
1931 perf_evsel__group_desc(evsel, buf, buflen);
1932 ev_name = buf;
1933
1934 for_each_group_member(pos, evsel) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03001935 struct hists *pos_hists = evsel__hists(pos);
1936
Namhyung Kimf2148332014-01-14 11:52:48 +09001937 if (symbol_conf.filter_relative) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03001938 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1939 nr_events += pos_hists->stats.total_non_filtered_period;
Namhyung Kimf2148332014-01-14 11:52:48 +09001940 } else {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03001941 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1942 nr_events += pos_hists->stats.total_period;
Namhyung Kimf2148332014-01-14 11:52:48 +09001943 }
Namhyung Kim717e2632013-01-22 18:09:44 +09001944 }
1945 }
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001946
Kan Liang9e207dd2015-08-11 06:30:49 -04001947 if (symbol_conf.show_ref_callgraph &&
1948 strstr(ev_name, "call-graph=no"))
1949 enable_ref = true;
Ashay Ranecc6862802012-04-05 21:01:01 -05001950 nr_samples = convert_unit(nr_samples, &unit);
1951 printed = scnprintf(bf, size,
Kan Liang9e207dd2015-08-11 06:30:49 -04001952 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1953 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
Ashay Ranecc6862802012-04-05 21:01:01 -05001954
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001955
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001956 if (hists->uid_filter_str)
Arnaldo Carvalho de Melo0d37aa32012-01-19 14:08:15 -02001957 printed += snprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03001958 ", UID: %s", hists->uid_filter_str);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001959 if (thread)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001960 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001961 ", Thread: %s(%d)",
Frederic Weisbeckerb9c51432013-09-11 14:46:56 +02001962 (thread->comm_set ? thread__comm_str(thread) : ""),
Adrian Hunter38051232013-07-04 16:20:31 +03001963 thread->tid);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001964 if (dso)
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03001965 printed += scnprintf(bf + printed, size - printed,
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001966 ", DSO: %s", dso->short_name);
Kan Liang84734b02015-09-04 10:45:45 -04001967 if (socket_id > -1)
Kan Liang21394d92015-09-04 10:45:44 -04001968 printed += scnprintf(bf + printed, size - printed,
Kan Liang84734b02015-09-04 10:45:45 -04001969 ", Processor Socket: %d", socket_id);
Taeung Song1e378eb2014-10-07 16:13:15 +09001970 if (!is_report_browser(hbt)) {
1971 struct perf_top *top = hbt->arg;
1972
1973 if (top->zero)
1974 printed += scnprintf(bf + printed, size - printed, " [z]");
1975 }
1976
Arnaldo Carvalho de Melo469917c2010-09-13 10:25:04 -03001977 return printed;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03001978}
1979
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001980static inline void free_popup_options(char **options, int n)
1981{
1982 int i;
1983
Arnaldo Carvalho de Melo04662522013-12-26 17:41:15 -03001984 for (i = 0; i < n; ++i)
1985 zfree(&options[i]);
Stephane Eranian24bff2d2012-03-12 16:13:30 +01001986}
1987
Feng Tang341487ab2013-02-03 14:38:20 +08001988/*
1989 * Only runtime switching of perf data file will make "input_name" point
1990 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1991 * whether we need to call free() for current "input_name" during the switch.
1992 */
1993static bool is_input_name_malloced = false;
1994
1995static int switch_data_file(void)
1996{
1997 char *pwd, *options[32], *abs_path[32], *tmp;
1998 DIR *pwd_dir;
1999 int nr_options = 0, choice = -1, ret = -1;
2000 struct dirent *dent;
2001
2002 pwd = getenv("PWD");
2003 if (!pwd)
2004 return ret;
2005
2006 pwd_dir = opendir(pwd);
2007 if (!pwd_dir)
2008 return ret;
2009
2010 memset(options, 0, sizeof(options));
2011 memset(options, 0, sizeof(abs_path));
2012
2013 while ((dent = readdir(pwd_dir))) {
2014 char path[PATH_MAX];
2015 u64 magic;
2016 char *name = dent->d_name;
2017 FILE *file;
2018
2019 if (!(dent->d_type == DT_REG))
2020 continue;
2021
2022 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2023
2024 file = fopen(path, "r");
2025 if (!file)
2026 continue;
2027
2028 if (fread(&magic, 1, 8, file) < 8)
2029 goto close_file_and_continue;
2030
2031 if (is_perf_magic(magic)) {
2032 options[nr_options] = strdup(name);
2033 if (!options[nr_options])
2034 goto close_file_and_continue;
2035
2036 abs_path[nr_options] = strdup(path);
2037 if (!abs_path[nr_options]) {
Arnaldo Carvalho de Melo74cf2492013-12-27 16:55:14 -03002038 zfree(&options[nr_options]);
Feng Tang341487ab2013-02-03 14:38:20 +08002039 ui__warning("Can't search all data files due to memory shortage.\n");
2040 fclose(file);
2041 break;
2042 }
2043
2044 nr_options++;
2045 }
2046
2047close_file_and_continue:
2048 fclose(file);
2049 if (nr_options >= 32) {
2050 ui__warning("Too many perf data files in PWD!\n"
2051 "Only the first 32 files will be listed.\n");
2052 break;
2053 }
2054 }
2055 closedir(pwd_dir);
2056
2057 if (nr_options) {
2058 choice = ui__popup_menu(nr_options, options);
2059 if (choice < nr_options && choice >= 0) {
2060 tmp = strdup(abs_path[choice]);
2061 if (tmp) {
2062 if (is_input_name_malloced)
2063 free((void *)input_name);
2064 input_name = tmp;
2065 is_input_name_malloced = true;
2066 ret = 0;
2067 } else
2068 ui__warning("Data switch failed due to memory shortage!\n");
2069 }
2070 }
2071
2072 free_popup_options(options, nr_options);
2073 free_popup_options(abs_path, nr_options);
2074 return ret;
2075}
2076
Namhyung Kimea7cd592015-04-22 16:18:19 +09002077struct popup_action {
2078 struct thread *thread;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002079 struct map_symbol ms;
Kan Liang84734b02015-09-04 10:45:45 -04002080 int socket;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002081
2082 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2083};
2084
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002085static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002086do_annotate(struct hist_browser *browser, struct popup_action *act)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002087{
2088 struct perf_evsel *evsel;
2089 struct annotation *notes;
2090 struct hist_entry *he;
2091 int err;
2092
Arnaldo Carvalho de Meloeebd0bf2015-09-08 15:52:20 -03002093 if (!objdump_path && perf_env__lookup_objdump(browser->env))
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002094 return 0;
2095
Namhyung Kimea7cd592015-04-22 16:18:19 +09002096 notes = symbol__annotation(act->ms.sym);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002097 if (!notes->src)
2098 return 0;
2099
2100 evsel = hists_to_evsel(browser->hists);
Namhyung Kimea7cd592015-04-22 16:18:19 +09002101 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002102 he = hist_browser__selected_entry(browser);
2103 /*
2104 * offer option to annotate the other branch source or target
2105 * (if they exists) when returning from annotate
2106 */
2107 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2108 return 1;
2109
2110 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2111 if (err)
2112 ui_browser__handle_resize(&browser->b);
2113 return 0;
2114}
2115
2116static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002117add_annotate_opt(struct hist_browser *browser __maybe_unused,
2118 struct popup_action *act, char **optstr,
2119 struct map *map, struct symbol *sym)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002120{
Namhyung Kimea7cd592015-04-22 16:18:19 +09002121 if (sym == NULL || map->dso->annotate_warned)
2122 return 0;
2123
2124 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2125 return 0;
2126
2127 act->ms.map = map;
2128 act->ms.sym = sym;
2129 act->fn = do_annotate;
2130 return 1;
2131}
2132
2133static int
2134do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2135{
2136 struct thread *thread = act->thread;
2137
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002138 if (browser->hists->thread_filter) {
2139 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2140 perf_hpp__set_elide(HISTC_THREAD, false);
2141 thread__zput(browser->hists->thread_filter);
2142 ui_helpline__pop();
2143 } else {
Arnaldo Carvalho de Melo7727a922015-10-12 13:56:50 -03002144 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002145 thread->comm_set ? thread__comm_str(thread) : "",
2146 thread->tid);
2147 browser->hists->thread_filter = thread__get(thread);
2148 perf_hpp__set_elide(HISTC_THREAD, false);
2149 pstack__push(browser->pstack, &browser->hists->thread_filter);
2150 }
2151
2152 hists__filter_by_thread(browser->hists);
2153 hist_browser__reset(browser);
2154 return 0;
2155}
2156
2157static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002158add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2159 char **optstr, struct thread *thread)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002160{
Namhyung Kim2eafd412016-01-21 19:13:24 -03002161 if (!sort__has_thread || thread == NULL)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002162 return 0;
2163
2164 if (asprintf(optstr, "Zoom %s %s(%d) thread",
2165 browser->hists->thread_filter ? "out of" : "into",
2166 thread->comm_set ? thread__comm_str(thread) : "",
2167 thread->tid) < 0)
2168 return 0;
2169
2170 act->thread = thread;
2171 act->fn = do_zoom_thread;
2172 return 1;
2173}
2174
2175static int
2176do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2177{
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002178 struct map *map = act->ms.map;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002179
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002180 if (browser->hists->dso_filter) {
2181 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2182 perf_hpp__set_elide(HISTC_DSO, false);
2183 browser->hists->dso_filter = NULL;
2184 ui_helpline__pop();
2185 } else {
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002186 if (map == NULL)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002187 return 0;
Arnaldo Carvalho de Melo7727a922015-10-12 13:56:50 -03002188 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002189 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2190 browser->hists->dso_filter = map->dso;
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002191 perf_hpp__set_elide(HISTC_DSO, true);
2192 pstack__push(browser->pstack, &browser->hists->dso_filter);
2193 }
2194
2195 hists__filter_by_dso(browser->hists);
2196 hist_browser__reset(browser);
2197 return 0;
2198}
2199
2200static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002201add_dso_opt(struct hist_browser *browser, struct popup_action *act,
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002202 char **optstr, struct map *map)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002203{
Namhyung Kimb1447a542016-01-22 11:22:41 -03002204 if (!sort__has_dso || map == NULL)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002205 return 0;
2206
2207 if (asprintf(optstr, "Zoom %s %s DSO",
2208 browser->hists->dso_filter ? "out of" : "into",
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002209 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002210 return 0;
2211
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002212 act->ms.map = map;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002213 act->fn = do_zoom_dso;
2214 return 1;
2215}
2216
2217static int
2218do_browse_map(struct hist_browser *browser __maybe_unused,
2219 struct popup_action *act)
2220{
2221 map__browse(act->ms.map);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002222 return 0;
2223}
2224
2225static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002226add_map_opt(struct hist_browser *browser __maybe_unused,
2227 struct popup_action *act, char **optstr, struct map *map)
2228{
Namhyung Kimb1447a542016-01-22 11:22:41 -03002229 if (!sort__has_dso || map == NULL)
Namhyung Kimea7cd592015-04-22 16:18:19 +09002230 return 0;
2231
2232 if (asprintf(optstr, "Browse map details") < 0)
2233 return 0;
2234
2235 act->ms.map = map;
2236 act->fn = do_browse_map;
2237 return 1;
2238}
2239
2240static int
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002241do_run_script(struct hist_browser *browser __maybe_unused,
Namhyung Kimea7cd592015-04-22 16:18:19 +09002242 struct popup_action *act)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002243{
2244 char script_opt[64];
2245 memset(script_opt, 0, sizeof(script_opt));
2246
Namhyung Kimea7cd592015-04-22 16:18:19 +09002247 if (act->thread) {
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002248 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
Namhyung Kimea7cd592015-04-22 16:18:19 +09002249 thread__comm_str(act->thread));
2250 } else if (act->ms.sym) {
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002251 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
Namhyung Kimea7cd592015-04-22 16:18:19 +09002252 act->ms.sym->name);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002253 }
2254
2255 script_browse(script_opt);
2256 return 0;
2257}
2258
2259static int
Namhyung Kimea7cd592015-04-22 16:18:19 +09002260add_script_opt(struct hist_browser *browser __maybe_unused,
2261 struct popup_action *act, char **optstr,
2262 struct thread *thread, struct symbol *sym)
2263{
2264 if (thread) {
2265 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2266 thread__comm_str(thread)) < 0)
2267 return 0;
2268 } else if (sym) {
2269 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2270 sym->name) < 0)
2271 return 0;
2272 } else {
2273 if (asprintf(optstr, "Run scripts for all samples") < 0)
2274 return 0;
2275 }
2276
2277 act->thread = thread;
2278 act->ms.sym = sym;
2279 act->fn = do_run_script;
2280 return 1;
2281}
2282
2283static int
2284do_switch_data(struct hist_browser *browser __maybe_unused,
2285 struct popup_action *act __maybe_unused)
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002286{
2287 if (switch_data_file()) {
2288 ui__warning("Won't switch the data files due to\n"
2289 "no valid data file get selected!\n");
Namhyung Kimea7cd592015-04-22 16:18:19 +09002290 return 0;
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002291 }
2292
2293 return K_SWITCH_INPUT_DATA;
2294}
2295
Namhyung Kimea7cd592015-04-22 16:18:19 +09002296static int
2297add_switch_opt(struct hist_browser *browser,
2298 struct popup_action *act, char **optstr)
2299{
2300 if (!is_report_browser(browser->hbt))
2301 return 0;
2302
2303 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2304 return 0;
2305
2306 act->fn = do_switch_data;
2307 return 1;
2308}
2309
2310static int
2311do_exit_browser(struct hist_browser *browser __maybe_unused,
2312 struct popup_action *act __maybe_unused)
2313{
2314 return 0;
2315}
2316
2317static int
2318add_exit_opt(struct hist_browser *browser __maybe_unused,
2319 struct popup_action *act, char **optstr)
2320{
2321 if (asprintf(optstr, "Exit") < 0)
2322 return 0;
2323
2324 act->fn = do_exit_browser;
2325 return 1;
2326}
2327
Kan Liang84734b02015-09-04 10:45:45 -04002328static int
2329do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2330{
2331 if (browser->hists->socket_filter > -1) {
2332 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2333 browser->hists->socket_filter = -1;
2334 perf_hpp__set_elide(HISTC_SOCKET, false);
2335 } else {
2336 browser->hists->socket_filter = act->socket;
2337 perf_hpp__set_elide(HISTC_SOCKET, true);
2338 pstack__push(browser->pstack, &browser->hists->socket_filter);
2339 }
2340
2341 hists__filter_by_socket(browser->hists);
2342 hist_browser__reset(browser);
2343 return 0;
2344}
2345
2346static int
2347add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2348 char **optstr, int socket_id)
2349{
Namhyung Kimd9695d92016-01-22 12:20:18 -03002350 if (!sort__has_socket || socket_id < 0)
Kan Liang84734b02015-09-04 10:45:45 -04002351 return 0;
2352
2353 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2354 (browser->hists->socket_filter > -1) ? "out of" : "into",
2355 socket_id) < 0)
2356 return 0;
2357
2358 act->socket = socket_id;
2359 act->fn = do_zoom_socket;
2360 return 1;
2361}
2362
Namhyung Kim112f7612014-04-22 14:05:35 +09002363static void hist_browser__update_nr_entries(struct hist_browser *hb)
Namhyung Kim064f1982013-05-14 11:09:04 +09002364{
2365 u64 nr_entries = 0;
2366 struct rb_node *nd = rb_first(&hb->hists->entries);
2367
Namhyung Kimf5b763f2016-02-25 00:13:43 +09002368 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
Namhyung Kim268397c2014-04-22 14:49:31 +09002369 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2370 return;
2371 }
2372
Namhyung Kim14135662013-10-31 10:17:39 +09002373 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
Namhyung Kim064f1982013-05-14 11:09:04 +09002374 nr_entries++;
Namhyung Kimf5b763f2016-02-25 00:13:43 +09002375 nd = rb_hierarchy_next(nd);
Namhyung Kim064f1982013-05-14 11:09:04 +09002376 }
2377
Namhyung Kim112f7612014-04-22 14:05:35 +09002378 hb->nr_non_filtered_entries = nr_entries;
Namhyung Kimf5b763f2016-02-25 00:13:43 +09002379 hb->nr_hierarchy_entries = nr_entries;
Namhyung Kim064f1982013-05-14 11:09:04 +09002380}
Feng Tang341487ab2013-02-03 14:38:20 +08002381
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002382static void hist_browser__update_percent_limit(struct hist_browser *hb,
2383 double percent)
2384{
2385 struct hist_entry *he;
2386 struct rb_node *nd = rb_first(&hb->hists->entries);
2387 u64 total = hists__total_period(hb->hists);
2388 u64 min_callchain_hits = total * (percent / 100);
2389
2390 hb->min_pcnt = callchain_param.min_percent = percent;
2391
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002392 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2393 he = rb_entry(nd, struct hist_entry, rb_node);
2394
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002395 if (!he->leaf || !symbol_conf.use_callchain)
2396 goto next;
2397
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002398 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2399 total = he->stat.period;
2400
2401 if (symbol_conf.cumulate_callchain)
2402 total = he->stat_acc->period;
2403
2404 min_callchain_hits = total * (percent / 100);
2405 }
2406
2407 callchain_param.sort(&he->sorted_chain, he->callchain,
2408 min_callchain_hits, &callchain_param);
2409
Namhyung Kimd0506ed2016-02-25 00:13:45 +09002410next:
2411 /*
2412 * Tentatively set unfolded so that the rb_hierarchy_next()
2413 * can toggle children of folded entries too.
2414 */
2415 he->unfolded = he->has_children;
2416 nd = rb_hierarchy_next(nd);
2417
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002418 /* force to re-evaluate folding state of callchains */
2419 he->init_have_children = false;
Namhyung Kim492b1012016-02-25 00:13:44 +09002420 hist_entry__set_folding(he, hb, false);
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002421 }
2422}
2423
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03002424static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
Jiri Olsadd00d482014-06-19 13:41:13 +02002425 const char *helpline,
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03002426 bool left_exits,
Namhyung Kim68d80752012-11-02 14:50:06 +09002427 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09002428 float min_pcnt,
Kan Liangce80d3b2015-08-28 05:48:04 -04002429 struct perf_env *env)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002430{
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002431 struct hists *hists = evsel__hists(evsel);
Namhyung Kimb1a9cee2015-04-22 16:18:17 +09002432 struct hist_browser *browser = hist_browser__new(hists, hbt, env);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002433 struct branch_info *bi;
Namhyung Kimf2b487d2015-04-22 16:18:14 +09002434#define MAX_OPTIONS 16
2435 char *options[MAX_OPTIONS];
Namhyung Kimea7cd592015-04-22 16:18:19 +09002436 struct popup_action actions[MAX_OPTIONS];
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002437 int nr_options = 0;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002438 int key = -1;
Namhyung Kim938a23a2012-03-16 17:50:53 +09002439 char buf[64];
Namhyung Kim9783adf2012-11-02 14:50:05 +09002440 int delay_secs = hbt ? hbt->refresh : 0;
Namhyung Kim59dc9f22014-07-31 14:47:41 +09002441 struct perf_hpp_fmt *fmt;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002442
Namhyung Kime8e684a2013-12-26 14:37:58 +09002443#define HIST_BROWSER_HELP_COMMON \
2444 "h/?/F1 Show this window\n" \
2445 "UP/DOWN/PGUP\n" \
2446 "PGDN/SPACE Navigate\n" \
2447 "q/ESC/CTRL+C Exit browser\n\n" \
2448 "For multiple event sessions:\n\n" \
2449 "TAB/UNTAB Switch events\n\n" \
2450 "For symbolic views (--sort has sym):\n\n" \
Arnaldo Carvalho de Melo7727a922015-10-12 13:56:50 -03002451 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2452 "ESC Zoom out\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09002453 "a Annotate current symbol\n" \
2454 "C Collapse all callchains\n" \
2455 "d Zoom into current DSO\n" \
2456 "E Expand all callchains\n" \
Namhyung Kim105eb302014-02-10 11:20:10 +09002457 "F Toggle percentage of filtered entries\n" \
Arnaldo Carvalho de Melo025bf7e2014-06-14 15:44:52 +02002458 "H Display column headers\n" \
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002459 "L Change percent limit\n" \
Namhyung Kim31eb4362015-10-13 09:02:01 +09002460 "m Display context menu\n" \
Kan Liang84734b02015-09-04 10:45:45 -04002461 "S Zoom into current Processor Socket\n" \
Namhyung Kime8e684a2013-12-26 14:37:58 +09002462
2463 /* help messages are sorted by lexical order of the hotkey */
2464 const char report_help[] = HIST_BROWSER_HELP_COMMON
Namhyung Kim6dd60132013-12-26 14:37:59 +09002465 "i Show header information\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09002466 "P Print histograms to perf.hist.N\n"
2467 "r Run available scripts\n"
2468 "s Switch to another data file in PWD\n"
2469 "t Zoom into current Thread\n"
2470 "V Verbose (DSO names in callchains, etc)\n"
2471 "/ Filter symbol by name";
2472 const char top_help[] = HIST_BROWSER_HELP_COMMON
2473 "P Print histograms to perf.hist.N\n"
2474 "t Zoom into current Thread\n"
2475 "V Verbose (DSO names in callchains, etc)\n"
Namhyung Kim42337a22014-08-12 17:16:06 +09002476 "z Toggle zeroing of samples\n"
Arnaldo Carvalho de Melofbb79972015-06-19 16:56:04 -03002477 "f Enable/Disable events\n"
Namhyung Kime8e684a2013-12-26 14:37:58 +09002478 "/ Filter symbol by name";
2479
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002480 if (browser == NULL)
2481 return -1;
2482
Namhyung Kimed426912015-05-29 21:53:44 +09002483 /* reset abort key so that it can get Ctrl-C as a key */
2484 SLang_reset_tty();
2485 SLang_init_tty(0, 0, 0);
2486
Namhyung Kim03905042015-11-28 02:32:39 +09002487 if (min_pcnt)
Namhyung Kim064f1982013-05-14 11:09:04 +09002488 browser->min_pcnt = min_pcnt;
Namhyung Kim03905042015-11-28 02:32:39 +09002489 hist_browser__update_nr_entries(browser);
Namhyung Kim064f1982013-05-14 11:09:04 +09002490
Kan Liang84734b02015-09-04 10:45:45 -04002491 browser->pstack = pstack__new(3);
Namhyung Kim01f00a12015-04-22 16:18:16 +09002492 if (browser->pstack == NULL)
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002493 goto out;
2494
2495 ui_helpline__push(helpline);
2496
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002497 memset(options, 0, sizeof(options));
Namhyung Kimea7cd592015-04-22 16:18:19 +09002498 memset(actions, 0, sizeof(actions));
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002499
Jiri Olsaf0786af2016-01-18 10:24:23 +01002500 hists__for_each_format(browser->hists, fmt) {
Namhyung Kim59dc9f22014-07-31 14:47:41 +09002501 perf_hpp__reset_width(fmt, hists);
Arnaldo Carvalho de Meloc6c3c022015-08-11 17:22:43 -03002502 /*
2503 * This is done just once, and activates the horizontal scrolling
2504 * code in the ui_browser code, it would be better to have a the
2505 * counter in the perf_hpp code, but I couldn't find doing it here
2506 * works, FIXME by setting this in hist_browser__new, for now, be
2507 * clever 8-)
2508 */
2509 ++browser->b.columns;
2510 }
Namhyung Kim59dc9f22014-07-31 14:47:41 +09002511
Namhyung Kim5b591662014-07-31 14:47:38 +09002512 if (symbol_conf.col_width_list_str)
2513 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2514
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002515 while (1) {
Arnaldo Carvalho de Melof3b623b2015-03-02 22:21:35 -03002516 struct thread *thread = NULL;
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002517 struct map *map = NULL;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002518 int choice = 0;
Kan Liang84734b02015-09-04 10:45:45 -04002519 int socked_id = -1;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002520
Stephane Eranian24bff2d2012-03-12 16:13:30 +01002521 nr_options = 0;
2522
Arnaldo Carvalho de Melo5f00b0f2015-06-19 17:30:20 -03002523 key = hist_browser__run(browser, helpline);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002524
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03002525 if (browser->he_selection != NULL) {
2526 thread = hist_browser__selected_thread(browser);
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002527 map = browser->selection->map;
Kan Liang84734b02015-09-04 10:45:45 -04002528 socked_id = browser->he_selection->socket;
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03002529 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002530 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002531 case K_TAB:
2532 case K_UNTAB:
David Aherne4419b82011-10-19 11:37:47 -06002533 if (nr_events == 1)
2534 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002535 /*
2536 * Exit the browser, let hists__browser_tree
2537 * go to the next or previous
2538 */
2539 goto out_free_stack;
2540 case 'a':
Arnaldo Carvalho de Melo9c796ec2013-04-26 14:28:46 -03002541 if (!sort__has_sym) {
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002542 ui_browser__warning(&browser->b, delay_secs * 2,
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02002543 "Annotation is only available for symbolic views, "
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002544 "include \"sym*\" in --sort to use it.");
Arnaldo Carvalho de Meloa6e51f92011-10-21 10:58:24 -02002545 continue;
2546 }
2547
Arnaldo Carvalho de Melo60098912011-03-04 21:19:21 -03002548 if (browser->selection == NULL ||
Lin Mingdb9a9cbc2011-04-08 14:31:26 +08002549 browser->selection->sym == NULL ||
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002550 browser->selection->map->dso->annotate_warned)
2551 continue;
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002552
Namhyung Kimea7cd592015-04-22 16:18:19 +09002553 actions->ms.map = browser->selection->map;
2554 actions->ms.sym = browser->selection->sym;
2555 do_annotate(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002556 continue;
Arnaldo Carvalho de Meloaff3f3f2012-06-07 19:31:28 -03002557 case 'P':
2558 hist_browser__dump(browser);
2559 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002560 case 'd':
Arnaldo Carvalho de Melofae00652015-11-12 15:59:26 -03002561 actions->ms.map = map;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002562 do_zoom_dso(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002563 continue;
Arnaldo Carvalho de Meloa7cb8862012-08-03 13:53:40 -03002564 case 'V':
2565 browser->show_dso = !browser->show_dso;
2566 continue;
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002567 case 't':
Namhyung Kimea7cd592015-04-22 16:18:19 +09002568 actions->thread = thread;
2569 do_zoom_thread(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002570 continue;
Kan Liang84734b02015-09-04 10:45:45 -04002571 case 'S':
2572 actions->socket = socked_id;
2573 do_zoom_socket(browser, actions);
2574 continue;
Arnaldo Carvalho de Melo5a5626b2012-05-09 12:21:22 -03002575 case '/':
Namhyung Kim938a23a2012-03-16 17:50:53 +09002576 if (ui_browser__input_window("Symbol to show",
Arnaldo Carvalho de Melo4aa8e4542015-10-12 14:02:29 -03002577 "Please enter the name of symbol you want to see.\n"
2578 "To remove the filter later, press / + ENTER.",
Namhyung Kim938a23a2012-03-16 17:50:53 +09002579 buf, "ENTER: OK, ESC: Cancel",
2580 delay_secs * 2) == K_ENTER) {
Arnaldo Carvalho de Melo05e8b082012-05-29 22:42:18 -03002581 hists->symbol_filter_str = *buf ? buf : NULL;
2582 hists__filter_by_symbol(hists);
Namhyung Kim938a23a2012-03-16 17:50:53 +09002583 hist_browser__reset(browser);
2584 }
2585 continue;
Feng Tangcdbab7c2012-10-30 11:56:06 +08002586 case 'r':
Namhyung Kimea7cd592015-04-22 16:18:19 +09002587 if (is_report_browser(hbt)) {
2588 actions->thread = NULL;
2589 actions->ms.sym = NULL;
2590 do_run_script(browser, actions);
2591 }
Feng Tangc77d8d72012-11-01 00:00:55 +08002592 continue;
Feng Tang341487ab2013-02-03 14:38:20 +08002593 case 's':
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002594 if (is_report_browser(hbt)) {
Namhyung Kimea7cd592015-04-22 16:18:19 +09002595 key = do_switch_data(browser, actions);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002596 if (key == K_SWITCH_INPUT_DATA)
2597 goto out_free_stack;
2598 }
Feng Tang341487ab2013-02-03 14:38:20 +08002599 continue;
Namhyung Kim6dd60132013-12-26 14:37:59 +09002600 case 'i':
2601 /* env->arch is NULL for live-mode (i.e. perf top) */
2602 if (env->arch)
2603 tui__header_window(env);
2604 continue;
Namhyung Kim105eb302014-02-10 11:20:10 +09002605 case 'F':
2606 symbol_conf.filter_relative ^= 1;
2607 continue;
Namhyung Kim42337a22014-08-12 17:16:06 +09002608 case 'z':
2609 if (!is_report_browser(hbt)) {
2610 struct perf_top *top = hbt->arg;
2611
2612 top->zero = !top->zero;
2613 }
2614 continue;
Namhyung Kimb62e8df2016-02-03 23:11:23 +09002615 case 'L':
2616 if (ui_browser__input_window("Percent Limit",
2617 "Please enter the value you want to hide entries under that percent.",
2618 buf, "ENTER: OK, ESC: Cancel",
2619 delay_secs * 2) == K_ENTER) {
2620 char *end;
2621 double new_percent = strtod(buf, &end);
2622
2623 if (new_percent < 0 || new_percent > 100) {
2624 ui_browser__warning(&browser->b, delay_secs * 2,
2625 "Invalid percent: %.2f", new_percent);
2626 continue;
2627 }
2628
2629 hist_browser__update_percent_limit(browser, new_percent);
2630 hist_browser__reset(browser);
2631 }
2632 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002633 case K_F1:
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002634 case 'h':
2635 case '?':
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02002636 ui_browser__help_window(&browser->b,
Namhyung Kime8e684a2013-12-26 14:37:58 +09002637 is_report_browser(hbt) ? report_help : top_help);
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002638 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002639 case K_ENTER:
2640 case K_RIGHT:
Namhyung Kim31eb4362015-10-13 09:02:01 +09002641 case 'm':
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002642 /* menu */
2643 break;
Arnaldo Carvalho de Melo63ab1742015-08-12 12:42:58 -03002644 case K_ESC:
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002645 case K_LEFT: {
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002646 const void *top;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002647
Namhyung Kim01f00a12015-04-22 16:18:16 +09002648 if (pstack__empty(browser->pstack)) {
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002649 /*
2650 * Go back to the perf_evsel_menu__run or other user
2651 */
2652 if (left_exits)
2653 goto out_free_stack;
Arnaldo Carvalho de Melo63ab1742015-08-12 12:42:58 -03002654
2655 if (key == K_ESC &&
2656 ui_browser__dialog_yesno(&browser->b,
2657 "Do you really want to exit?"))
2658 goto out_free_stack;
2659
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002660 continue;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002661 }
Namhyung Kim64221842015-04-24 10:15:33 +09002662 top = pstack__peek(browser->pstack);
Namhyung Kimbc7cad42015-04-22 16:18:18 +09002663 if (top == &browser->hists->dso_filter) {
Namhyung Kim64221842015-04-24 10:15:33 +09002664 /*
2665 * No need to set actions->dso here since
2666 * it's just to remove the current filter.
2667 * Ditto for thread below.
2668 */
2669 do_zoom_dso(browser, actions);
Kan Liang84734b02015-09-04 10:45:45 -04002670 } else if (top == &browser->hists->thread_filter) {
Namhyung Kim64221842015-04-24 10:15:33 +09002671 do_zoom_thread(browser, actions);
Kan Liang84734b02015-09-04 10:45:45 -04002672 } else if (top == &browser->hists->socket_filter) {
2673 do_zoom_socket(browser, actions);
2674 }
Arnaldo Carvalho de Melob50e0032010-08-11 10:07:43 -03002675 continue;
2676 }
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002677 case 'q':
2678 case CTRL('c'):
Arnaldo Carvalho de Melo516e5362015-06-19 16:59:43 -03002679 goto out_free_stack;
Arnaldo Carvalho de Melofbb79972015-06-19 16:56:04 -03002680 case 'f':
Namhyung Kim13d1e532015-06-21 12:41:16 +09002681 if (!is_report_browser(hbt)) {
2682 struct perf_top *top = hbt->arg;
2683
2684 perf_evlist__toggle_enable(top->evlist);
2685 /*
2686 * No need to refresh, resort/decay histogram
2687 * entries if we are not collecting samples:
2688 */
2689 if (top->evlist->enabled) {
2690 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2691 hbt->refresh = delay_secs;
2692 } else {
2693 helpline = "Press 'f' again to re-enable the events";
2694 hbt->refresh = 0;
2695 }
2696 continue;
2697 }
Arnaldo Carvalho de Melo3e323dc2015-06-19 17:49:29 -03002698 /* Fall thru */
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002699 default:
Arnaldo Carvalho de Melo3e323dc2015-06-19 17:49:29 -03002700 helpline = "Press '?' for help on key bindings";
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002701 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002702 }
2703
Namhyung Kim40561322016-01-22 12:26:06 -03002704 if (!sort__has_sym || browser->selection == NULL)
Arnaldo Carvalho de Melo0ba332f2015-03-16 17:41:52 -03002705 goto skip_annotation;
2706
Namhyung Kim55369fc2013-04-01 20:35:20 +09002707 if (sort__mode == SORT_MODE__BRANCH) {
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002708 bi = browser->he_selection->branch_info;
Arnaldo Carvalho de Melo0ba332f2015-03-16 17:41:52 -03002709
2710 if (bi == NULL)
2711 goto skip_annotation;
2712
Namhyung Kimea7cd592015-04-22 16:18:19 +09002713 nr_options += add_annotate_opt(browser,
2714 &actions[nr_options],
2715 &options[nr_options],
2716 bi->from.map,
2717 bi->from.sym);
2718 if (bi->to.sym != bi->from.sym)
2719 nr_options += add_annotate_opt(browser,
2720 &actions[nr_options],
2721 &options[nr_options],
2722 bi->to.map,
2723 bi->to.sym);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002724 } else {
Namhyung Kimea7cd592015-04-22 16:18:19 +09002725 nr_options += add_annotate_opt(browser,
2726 &actions[nr_options],
2727 &options[nr_options],
2728 browser->selection->map,
2729 browser->selection->sym);
Stephane Eraniana68c2c52012-03-08 23:47:48 +01002730 }
Arnaldo Carvalho de Melo0ba332f2015-03-16 17:41:52 -03002731skip_annotation:
Namhyung Kimea7cd592015-04-22 16:18:19 +09002732 nr_options += add_thread_opt(browser, &actions[nr_options],
2733 &options[nr_options], thread);
2734 nr_options += add_dso_opt(browser, &actions[nr_options],
Arnaldo Carvalho de Melo045b80d2015-09-23 15:38:55 -03002735 &options[nr_options], map);
Namhyung Kimea7cd592015-04-22 16:18:19 +09002736 nr_options += add_map_opt(browser, &actions[nr_options],
2737 &options[nr_options],
Wang Nanbd315aa2015-09-14 10:23:55 +00002738 browser->selection ?
2739 browser->selection->map : NULL);
Kan Liang84734b02015-09-04 10:45:45 -04002740 nr_options += add_socket_opt(browser, &actions[nr_options],
2741 &options[nr_options],
2742 socked_id);
Feng Tangcdbab7c2012-10-30 11:56:06 +08002743 /* perf script support */
Namhyung Kimb1baae82016-01-26 15:37:30 -03002744 if (!is_report_browser(hbt))
2745 goto skip_scripting;
2746
Feng Tangcdbab7c2012-10-30 11:56:06 +08002747 if (browser->he_selection) {
Namhyung Kim2eafd412016-01-21 19:13:24 -03002748 if (sort__has_thread && thread) {
2749 nr_options += add_script_opt(browser,
2750 &actions[nr_options],
2751 &options[nr_options],
2752 thread, NULL);
2753 }
Wang Nanbd315aa2015-09-14 10:23:55 +00002754 /*
2755 * Note that browser->selection != NULL
2756 * when browser->he_selection is not NULL,
2757 * so we don't need to check browser->selection
2758 * before fetching browser->selection->sym like what
2759 * we do before fetching browser->selection->map.
2760 *
2761 * See hist_browser__show_entry.
2762 */
Namhyung Kimc221acb2016-01-21 19:50:09 -03002763 if (sort__has_sym && browser->selection->sym) {
2764 nr_options += add_script_opt(browser,
2765 &actions[nr_options],
2766 &options[nr_options],
2767 NULL, browser->selection->sym);
2768 }
Feng Tangcdbab7c2012-10-30 11:56:06 +08002769 }
Namhyung Kimea7cd592015-04-22 16:18:19 +09002770 nr_options += add_script_opt(browser, &actions[nr_options],
2771 &options[nr_options], NULL, NULL);
2772 nr_options += add_switch_opt(browser, &actions[nr_options],
2773 &options[nr_options]);
Namhyung Kimb1baae82016-01-26 15:37:30 -03002774skip_scripting:
Namhyung Kimea7cd592015-04-22 16:18:19 +09002775 nr_options += add_exit_opt(browser, &actions[nr_options],
2776 &options[nr_options]);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002777
Namhyung Kimea7cd592015-04-22 16:18:19 +09002778 do {
2779 struct popup_action *act;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002780
Namhyung Kimea7cd592015-04-22 16:18:19 +09002781 choice = ui__popup_menu(nr_options, options);
2782 if (choice == -1 || choice >= nr_options)
Feng Tang341487ab2013-02-03 14:38:20 +08002783 break;
Namhyung Kimea7cd592015-04-22 16:18:19 +09002784
2785 act = &actions[choice];
2786 key = act->fn(browser, act);
2787 } while (key == 1);
2788
2789 if (key == K_SWITCH_INPUT_DATA)
2790 break;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002791 }
2792out_free_stack:
Namhyung Kim01f00a12015-04-22 16:18:16 +09002793 pstack__delete(browser->pstack);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002794out:
2795 hist_browser__delete(browser);
Namhyung Kimf2b487d2015-04-22 16:18:14 +09002796 free_popup_options(options, MAX_OPTIONS);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002797 return key;
2798}
2799
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002800struct perf_evsel_menu {
2801 struct ui_browser b;
2802 struct perf_evsel *selection;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002803 bool lost_events, lost_events_warned;
Namhyung Kim064f1982013-05-14 11:09:04 +09002804 float min_pcnt;
Kan Liangce80d3b2015-08-28 05:48:04 -04002805 struct perf_env *env;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002806};
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002807
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002808static void perf_evsel_menu__write(struct ui_browser *browser,
2809 void *entry, int row)
2810{
2811 struct perf_evsel_menu *menu = container_of(browser,
2812 struct perf_evsel_menu, b);
2813 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002814 struct hists *hists = evsel__hists(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002815 bool current_entry = ui_browser__is_current_entry(browser, row);
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002816 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03002817 const char *ev_name = perf_evsel__name(evsel);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002818 char bf[256], unit;
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002819 const char *warn = " ";
2820 size_t printed;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002821
2822 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2823 HE_COLORSET_NORMAL);
2824
Namhyung Kim759ff492013-03-05 14:53:26 +09002825 if (perf_evsel__is_group_event(evsel)) {
Namhyung Kim717e2632013-01-22 18:09:44 +09002826 struct perf_evsel *pos;
2827
2828 ev_name = perf_evsel__group_name(evsel);
2829
2830 for_each_group_member(pos, evsel) {
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002831 struct hists *pos_hists = evsel__hists(pos);
2832 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
Namhyung Kim717e2632013-01-22 18:09:44 +09002833 }
2834 }
2835
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002836 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03002837 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002838 unit, unit == ' ' ? "" : " ", ev_name);
Arnaldo Carvalho de Melo517dfdb2015-08-11 12:50:55 -03002839 ui_browser__printf(browser, "%s", bf);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002840
Arnaldo Carvalho de Melo4ea062ed2014-10-09 13:13:41 -03002841 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002842 if (nr_events != 0) {
2843 menu->lost_events = true;
2844 if (!current_entry)
2845 ui_browser__set_color(browser, HE_COLORSET_TOP);
2846 nr_events = convert_unit(nr_events, &unit);
Arnaldo Carvalho de Meloe7f01d12012-03-14 12:29:29 -03002847 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2848 nr_events, unit, unit == ' ' ? "" : " ");
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002849 warn = bf;
2850 }
2851
Arnaldo Carvalho de Melo26270a02015-08-11 12:24:27 -03002852 ui_browser__write_nstring(browser, warn, browser->width - printed);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002853
2854 if (current_entry)
2855 menu->selection = evsel;
2856}
2857
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03002858static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2859 int nr_events, const char *help,
Namhyung Kim9783adf2012-11-02 14:50:05 +09002860 struct hist_browser_timer *hbt)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002861{
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002862 struct perf_evlist *evlist = menu->b.priv;
2863 struct perf_evsel *pos;
Jiri Olsadd00d482014-06-19 13:41:13 +02002864 const char *title = "Available samples";
Namhyung Kim9783adf2012-11-02 14:50:05 +09002865 int delay_secs = hbt ? hbt->refresh : 0;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002866 int key;
2867
2868 if (ui_browser__show(&menu->b, title,
2869 "ESC: exit, ENTER|->: Browse histograms") < 0)
2870 return -1;
2871
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002872 while (1) {
Arnaldo Carvalho de Melo3af6e332011-10-13 08:52:46 -03002873 key = ui_browser__run(&menu->b, delay_secs);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002874
2875 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002876 case K_TIMER:
Namhyung Kim9783adf2012-11-02 14:50:05 +09002877 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo7b275092011-10-29 12:15:04 -02002878
2879 if (!menu->lost_events_warned && menu->lost_events) {
2880 ui_browser__warn_lost_events(&menu->b);
2881 menu->lost_events_warned = true;
2882 }
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03002883 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002884 case K_RIGHT:
2885 case K_ENTER:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002886 if (!menu->selection)
2887 continue;
2888 pos = menu->selection;
2889browse_hists:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002890 perf_evlist__set_selected(evlist, pos);
2891 /*
2892 * Give the calling tool a chance to populate the non
2893 * default evsel resorted hists tree.
2894 */
Namhyung Kim9783adf2012-11-02 14:50:05 +09002895 if (hbt)
2896 hbt->timer(hbt->arg);
Arnaldo Carvalho de Melo34958542011-10-05 19:35:54 -03002897 key = perf_evsel__hists_browse(pos, nr_events, help,
Jiri Olsadd00d482014-06-19 13:41:13 +02002898 true, hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09002899 menu->min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09002900 menu->env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002901 ui_browser__show_title(&menu->b, title);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002902 switch (key) {
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002903 case K_TAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002904 if (pos->node.next == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03002905 pos = perf_evlist__first(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002906 else
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03002907 pos = perf_evsel__next(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002908 goto browse_hists;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002909 case K_UNTAB:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002910 if (pos->node.prev == &evlist->entries)
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03002911 pos = perf_evlist__last(evlist);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002912 else
Arnaldo Carvalho de Melod87fcb42013-11-13 15:56:40 -03002913 pos = perf_evsel__prev(pos);
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002914 goto browse_hists;
Feng Tang341487ab2013-02-03 14:38:20 +08002915 case K_SWITCH_INPUT_DATA:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002916 case 'q':
2917 case CTRL('c'):
2918 goto out;
Arnaldo Carvalho de Melo63ab1742015-08-12 12:42:58 -03002919 case K_ESC:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002920 default:
2921 continue;
2922 }
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002923 case K_LEFT:
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002924 continue;
Arnaldo Carvalho de Melocf958002011-10-20 16:59:15 -02002925 case K_ESC:
Arnaldo Carvalho de Melo4610e412011-10-26 12:04:37 -02002926 if (!ui_browser__dialog_yesno(&menu->b,
2927 "Do you really want to exit?"))
Arnaldo Carvalho de Meloed7e5662011-10-13 08:31:22 -03002928 continue;
2929 /* Fall thru */
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002930 case 'q':
2931 case CTRL('c'):
2932 goto out;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002933 default:
Arnaldo Carvalho de Melo18eaf0b2011-10-13 12:22:28 -03002934 continue;
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03002935 }
2936 }
2937
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002938out:
2939 ui_browser__hide(&menu->b);
2940 return key;
2941}
2942
Arnaldo Carvalho de Melo316c7132013-11-05 15:32:36 -03002943static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09002944 void *entry)
2945{
2946 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2947
2948 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2949 return true;
2950
2951 return false;
2952}
2953
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002954static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09002955 int nr_entries, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09002956 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09002957 float min_pcnt,
Kan Liangce80d3b2015-08-28 05:48:04 -04002958 struct perf_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002959{
2960 struct perf_evsel *pos;
2961 struct perf_evsel_menu menu = {
2962 .b = {
2963 .entries = &evlist->entries,
2964 .refresh = ui_browser__list_head_refresh,
2965 .seek = ui_browser__list_head_seek,
2966 .write = perf_evsel_menu__write,
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09002967 .filter = filter_group_entries,
2968 .nr_entries = nr_entries,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002969 .priv = evlist,
2970 },
Namhyung Kim064f1982013-05-14 11:09:04 +09002971 .min_pcnt = min_pcnt,
Namhyung Kim68d80752012-11-02 14:50:06 +09002972 .env = env,
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002973 };
2974
2975 ui_helpline__push("Press ESC to exit");
2976
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03002977 evlist__for_each(evlist, pos) {
Arnaldo Carvalho de Melo7289f832012-06-12 12:34:58 -03002978 const char *ev_name = perf_evsel__name(pos);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002979 size_t line_len = strlen(ev_name) + 7;
2980
2981 if (menu.b.width < line_len)
2982 menu.b.width = line_len;
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002983 }
2984
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09002985 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002986}
2987
Arnaldo Carvalho de Melo81cce8d2011-10-05 19:11:32 -03002988int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
Namhyung Kim68d80752012-11-02 14:50:06 +09002989 struct hist_browser_timer *hbt,
Namhyung Kim064f1982013-05-14 11:09:04 +09002990 float min_pcnt,
Kan Liangce80d3b2015-08-28 05:48:04 -04002991 struct perf_env *env)
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03002992{
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09002993 int nr_entries = evlist->nr_entries;
2994
2995single_entry:
2996 if (nr_entries == 1) {
Arnaldo Carvalho de Melo9a354cd2013-11-13 15:54:30 -03002997 struct perf_evsel *first = perf_evlist__first(evlist);
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09002998
2999 return perf_evsel__hists_browse(first, nr_entries, help,
Jiri Olsadd00d482014-06-19 13:41:13 +02003000 false, hbt, min_pcnt,
Namhyung Kim064f1982013-05-14 11:09:04 +09003001 env);
Arnaldo Carvalho de Melo7f0030b2011-03-06 13:07:30 -03003002 }
3003
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003004 if (symbol_conf.event_group) {
3005 struct perf_evsel *pos;
3006
3007 nr_entries = 0;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03003008 evlist__for_each(evlist, pos) {
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003009 if (perf_evsel__is_group_leader(pos))
3010 nr_entries++;
Arnaldo Carvalho de Melo0050f7a2014-01-10 10:37:27 -03003011 }
Namhyung Kimfc24d7c2013-01-22 18:09:43 +09003012
3013 if (nr_entries == 1)
3014 goto single_entry;
3015 }
3016
3017 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
Namhyung Kim064f1982013-05-14 11:09:04 +09003018 hbt, min_pcnt, env);
Arnaldo Carvalho de Melod1b4f242010-08-10 15:49:07 -03003019}