aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorGrant Likely <grant.likely@secretlab.ca>2010-12-30 00:20:30 -0500
committerGrant Likely <grant.likely@secretlab.ca>2010-12-30 00:21:47 -0500
commitd392da5207352f09030e95d9ea335a4225667ec0 (patch)
tree7d6cd1932afcad0a5619a5c504a6d93ca318187c /tools/perf/util
parente39d5ef678045d61812c1401f04fe8edb14d6359 (diff)
parent387c31c7e5c9805b0aef8833d1731a5fe7bdea14 (diff)
Merge v2.6.37-rc8 into powerpc/next
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/cache.h2
-rw-r--r--tools/perf/util/callchain.c98
-rw-r--r--tools/perf/util/callchain.h26
-rw-r--r--tools/perf/util/debug.c6
-rw-r--r--tools/perf/util/debug.h11
-rw-r--r--tools/perf/util/header.c21
-rw-r--r--tools/perf/util/hist.c20
-rw-r--r--tools/perf/util/hist.h3
-rw-r--r--tools/perf/util/include/linux/list.h8
-rw-r--r--tools/perf/util/include/linux/types.h12
-rw-r--r--tools/perf/util/map.h10
-rw-r--r--tools/perf/util/newt.c1568
-rw-r--r--tools/perf/util/path.c3
-rw-r--r--tools/perf/util/probe-event.c210
-rw-r--r--tools/perf/util/probe-event.h16
-rw-r--r--tools/perf/util/probe-finder.c703
-rw-r--r--tools/perf/util/probe-finder.h31
-rw-r--r--tools/perf/util/pstack.h2
-rw-r--r--tools/perf/util/sort.c6
-rw-r--r--tools/perf/util/sort.h2
-rw-r--r--tools/perf/util/string.c2
-rw-r--r--tools/perf/util/symbol.c128
-rw-r--r--tools/perf/util/symbol.h7
-rw-r--r--tools/perf/util/trace-event-scripting.c4
-rw-r--r--tools/perf/util/ui/browser.c337
-rw-r--r--tools/perf/util/ui/browser.h51
-rw-r--r--tools/perf/util/ui/browsers/annotate.c237
-rw-r--r--tools/perf/util/ui/browsers/hists.c1013
-rw-r--r--tools/perf/util/ui/browsers/map.c155
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/helpline.c69
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c42
-rw-r--r--tools/perf/util/ui/util.c113
-rw-r--r--tools/perf/util/ui/util.h10
-rw-r--r--tools/perf/util/util.h13
39 files changed, 3204 insertions, 1850 deletions
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 27e9ebe4076e..a7729797fd96 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -82,6 +82,8 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2
82extern char *perf_pathdup(const char *fmt, ...) 82extern char *perf_pathdup(const char *fmt, ...)
83 __attribute__((format (printf, 1, 2))); 83 __attribute__((format (printf, 1, 2)));
84 84
85#ifdef NO_STRLCPY
85extern size_t strlcpy(char *dest, const char *src, size_t size); 86extern size_t strlcpy(char *dest, const char *src, size_t size);
87#endif
86 88
87#endif /* __PERF_CACHE_H */ 89#endif /* __PERF_CACHE_H */
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index f231f43424d2..e12d539417b2 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -28,6 +28,9 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
28#define chain_for_each_child(child, parent) \ 28#define chain_for_each_child(child, parent) \
29 list_for_each_entry(child, &parent->children, brothers) 29 list_for_each_entry(child, &parent->children, brothers)
30 30
31#define chain_for_each_child_safe(child, next, parent) \
32 list_for_each_entry_safe(child, next, &parent->children, brothers)
33
31static void 34static void
32rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, 35rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
33 enum chain_mode mode) 36 enum chain_mode mode)
@@ -86,10 +89,10 @@ __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
86 * sort them by hit 89 * sort them by hit
87 */ 90 */
88static void 91static void
89sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, 92sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
90 u64 min_hit, struct callchain_param *param __used) 93 u64 min_hit, struct callchain_param *param __used)
91{ 94{
92 __sort_chain_flat(rb_root, node, min_hit); 95 __sort_chain_flat(rb_root, &root->node, min_hit);
93} 96}
94 97
95static void __sort_chain_graph_abs(struct callchain_node *node, 98static void __sort_chain_graph_abs(struct callchain_node *node,
@@ -108,11 +111,11 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
108} 111}
109 112
110static void 113static void
111sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root, 114sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
112 u64 min_hit, struct callchain_param *param __used) 115 u64 min_hit, struct callchain_param *param __used)
113{ 116{
114 __sort_chain_graph_abs(chain_root, min_hit); 117 __sort_chain_graph_abs(&chain_root->node, min_hit);
115 rb_root->rb_node = chain_root->rb_root.rb_node; 118 rb_root->rb_node = chain_root->node.rb_root.rb_node;
116} 119}
117 120
118static void __sort_chain_graph_rel(struct callchain_node *node, 121static void __sort_chain_graph_rel(struct callchain_node *node,
@@ -133,11 +136,11 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
133} 136}
134 137
135static void 138static void
136sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, 139sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
137 u64 min_hit __used, struct callchain_param *param) 140 u64 min_hit __used, struct callchain_param *param)
138{ 141{
139 __sort_chain_graph_rel(chain_root, param->min_percent / 100.0); 142 __sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
140 rb_root->rb_node = chain_root->rb_root.rb_node; 143 rb_root->rb_node = chain_root->node.rb_root.rb_node;
141} 144}
142 145
143int register_callchain_param(struct callchain_param *param) 146int register_callchain_param(struct callchain_param *param)
@@ -284,19 +287,18 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
284} 287}
285 288
286static int 289static int
287__append_chain(struct callchain_node *root, struct resolved_chain *chain, 290append_chain(struct callchain_node *root, struct resolved_chain *chain,
288 unsigned int start, u64 period); 291 unsigned int start, u64 period);
289 292
290static void 293static void
291__append_chain_children(struct callchain_node *root, 294append_chain_children(struct callchain_node *root, struct resolved_chain *chain,
292 struct resolved_chain *chain, 295 unsigned int start, u64 period)
293 unsigned int start, u64 period)
294{ 296{
295 struct callchain_node *rnode; 297 struct callchain_node *rnode;
296 298
297 /* lookup in childrens */ 299 /* lookup in childrens */
298 chain_for_each_child(rnode, root) { 300 chain_for_each_child(rnode, root) {
299 unsigned int ret = __append_chain(rnode, chain, start, period); 301 unsigned int ret = append_chain(rnode, chain, start, period);
300 302
301 if (!ret) 303 if (!ret)
302 goto inc_children_hit; 304 goto inc_children_hit;
@@ -309,8 +311,8 @@ inc_children_hit:
309} 311}
310 312
311static int 313static int
312__append_chain(struct callchain_node *root, struct resolved_chain *chain, 314append_chain(struct callchain_node *root, struct resolved_chain *chain,
313 unsigned int start, u64 period) 315 unsigned int start, u64 period)
314{ 316{
315 struct callchain_list *cnode; 317 struct callchain_list *cnode;
316 unsigned int i = start; 318 unsigned int i = start;
@@ -357,7 +359,7 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain,
357 } 359 }
358 360
359 /* We match the node and still have a part remaining */ 361 /* We match the node and still have a part remaining */
360 __append_chain_children(root, chain, i, period); 362 append_chain_children(root, chain, i, period);
361 363
362 return 0; 364 return 0;
363} 365}
@@ -380,8 +382,8 @@ static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
380} 382}
381 383
382 384
383int append_chain(struct callchain_node *root, struct ip_callchain *chain, 385int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
384 struct map_symbol *syms, u64 period) 386 struct map_symbol *syms, u64 period)
385{ 387{
386 struct resolved_chain *filtered; 388 struct resolved_chain *filtered;
387 389
@@ -398,9 +400,65 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain,
398 if (!filtered->nr) 400 if (!filtered->nr)
399 goto end; 401 goto end;
400 402
401 __append_chain_children(root, filtered, 0, period); 403 append_chain_children(&root->node, filtered, 0, period);
404
405 if (filtered->nr > root->max_depth)
406 root->max_depth = filtered->nr;
402end: 407end:
403 free(filtered); 408 free(filtered);
404 409
405 return 0; 410 return 0;
406} 411}
412
413static int
414merge_chain_branch(struct callchain_node *dst, struct callchain_node *src,
415 struct resolved_chain *chain)
416{
417 struct callchain_node *child, *next_child;
418 struct callchain_list *list, *next_list;
419 int old_pos = chain->nr;
420 int err = 0;
421
422 list_for_each_entry_safe(list, next_list, &src->val, list) {
423 chain->ips[chain->nr].ip = list->ip;
424 chain->ips[chain->nr].ms = list->ms;
425 chain->nr++;
426 list_del(&list->list);
427 free(list);
428 }
429
430 if (src->hit)
431 append_chain_children(dst, chain, 0, src->hit);
432
433 chain_for_each_child_safe(child, next_child, src) {
434 err = merge_chain_branch(dst, child, chain);
435 if (err)
436 break;
437
438 list_del(&child->brothers);
439 free(child);
440 }
441
442 chain->nr = old_pos;
443
444 return err;
445}
446
447int callchain_merge(struct callchain_root *dst, struct callchain_root *src)
448{
449 struct resolved_chain *chain;
450 int err;
451
452 chain = malloc(sizeof(*chain) +
453 src->max_depth * sizeof(struct resolved_ip));
454 if (!chain)
455 return -ENOMEM;
456
457 chain->nr = 0;
458
459 err = merge_chain_branch(&dst->node, &src->node, chain);
460
461 free(chain);
462
463 return err;
464}
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 624a96c636fd..c15fb8c24ad2 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -26,9 +26,14 @@ struct callchain_node {
26 u64 children_hit; 26 u64 children_hit;
27}; 27};
28 28
29struct callchain_root {
30 u64 max_depth;
31 struct callchain_node node;
32};
33
29struct callchain_param; 34struct callchain_param;
30 35
31typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, 36typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
32 u64, struct callchain_param *); 37 u64, struct callchain_param *);
33 38
34struct callchain_param { 39struct callchain_param {
@@ -44,14 +49,16 @@ struct callchain_list {
44 struct list_head list; 49 struct list_head list;
45}; 50};
46 51
47static inline void callchain_init(struct callchain_node *node) 52static inline void callchain_init(struct callchain_root *root)
48{ 53{
49 INIT_LIST_HEAD(&node->brothers); 54 INIT_LIST_HEAD(&root->node.brothers);
50 INIT_LIST_HEAD(&node->children); 55 INIT_LIST_HEAD(&root->node.children);
51 INIT_LIST_HEAD(&node->val); 56 INIT_LIST_HEAD(&root->node.val);
52 57
53 node->parent = NULL; 58 root->node.parent = NULL;
54 node->hit = 0; 59 root->node.hit = 0;
60 root->node.children_hit = 0;
61 root->max_depth = 0;
55} 62}
56 63
57static inline u64 cumul_hits(struct callchain_node *node) 64static inline u64 cumul_hits(struct callchain_node *node)
@@ -60,8 +67,9 @@ static inline u64 cumul_hits(struct callchain_node *node)
60} 67}
61 68
62int register_callchain_param(struct callchain_param *param); 69int register_callchain_param(struct callchain_param *param);
63int append_chain(struct callchain_node *root, struct ip_callchain *chain, 70int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
64 struct map_symbol *syms, u64 period); 71 struct map_symbol *syms, u64 period);
72int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
65 73
66bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); 74bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
67#endif /* __PERF_CALLCHAIN_H */ 75#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 318dab15d177..c8d81b00089d 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -12,8 +12,8 @@
12#include "debug.h" 12#include "debug.h"
13#include "util.h" 13#include "util.h"
14 14
15int verbose = 0; 15int verbose;
16bool dump_trace = false; 16bool dump_trace = false, quiet = false;
17 17
18int eprintf(int level, const char *fmt, ...) 18int eprintf(int level, const char *fmt, ...)
19{ 19{
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
23 if (verbose >= level) { 23 if (verbose >= level) {
24 va_start(args, fmt); 24 va_start(args, fmt);
25 if (use_browser > 0) 25 if (use_browser > 0)
26 ret = browser__show_help(fmt, args); 26 ret = ui_helpline__show_help(fmt, args);
27 else 27 else
28 ret = vfprintf(stderr, fmt, args); 28 ret = vfprintf(stderr, fmt, args);
29 va_end(args); 29 va_end(args);
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 047ac3324ebe..7b514082bbaf 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -6,7 +6,7 @@
6#include "event.h" 6#include "event.h"
7 7
8extern int verbose; 8extern int verbose;
9extern bool dump_trace; 9extern bool quiet, dump_trace;
10 10
11int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 11int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
12void trace_event(event_t *event); 12void trace_event(event_t *event);
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
14struct ui_progress; 14struct ui_progress;
15 15
16#ifdef NO_NEWT_SUPPORT 16#ifdef NO_NEWT_SUPPORT
17static inline int browser__show_help(const char *format __used, va_list ap __used) 17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{ 18{
19 return 0; 19 return 0;
20} 20}
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
30 30
31static inline void ui_progress__delete(struct ui_progress *self __used) {} 31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else 32#else
33int browser__show_help(const char *format, va_list ap); 33extern char ui_helpline__last_msg[];
34struct ui_progress *ui_progress__new(const char *title, u64 total); 34int ui_helpline__show_help(const char *format, va_list ap);
35void ui_progress__update(struct ui_progress *self, u64 curr); 35#include "ui/progress.h"
36void ui_progress__delete(struct ui_progress *self);
37#endif 36#endif
38 37
39#endif /* __PERF_DEBUG_H */ 38#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index d7e67b167ea3..7cba0551a565 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -265,15 +265,16 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
265 const char *name, bool is_kallsyms) 265 const char *name, bool is_kallsyms)
266{ 266{
267 const size_t size = PATH_MAX; 267 const size_t size = PATH_MAX;
268 char *filename = malloc(size), 268 char *realname = realpath(name, NULL),
269 *filename = malloc(size),
269 *linkname = malloc(size), *targetname; 270 *linkname = malloc(size), *targetname;
270 int len, err = -1; 271 int len, err = -1;
271 272
272 if (filename == NULL || linkname == NULL) 273 if (realname == NULL || filename == NULL || linkname == NULL)
273 goto out_free; 274 goto out_free;
274 275
275 len = snprintf(filename, size, "%s%s%s", 276 len = snprintf(filename, size, "%s%s%s",
276 debugdir, is_kallsyms ? "/" : "", name); 277 debugdir, is_kallsyms ? "/" : "", realname);
277 if (mkdir_p(filename, 0755)) 278 if (mkdir_p(filename, 0755))
278 goto out_free; 279 goto out_free;
279 280
@@ -283,7 +284,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
283 if (is_kallsyms) { 284 if (is_kallsyms) {
284 if (copyfile("/proc/kallsyms", filename)) 285 if (copyfile("/proc/kallsyms", filename))
285 goto out_free; 286 goto out_free;
286 } else if (link(name, filename) && copyfile(name, filename)) 287 } else if (link(realname, filename) && copyfile(name, filename))
287 goto out_free; 288 goto out_free;
288 } 289 }
289 290
@@ -300,6 +301,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
300 if (symlink(targetname, linkname) == 0) 301 if (symlink(targetname, linkname) == 0)
301 err = 0; 302 err = 0;
302out_free: 303out_free:
304 free(realname);
303 free(filename); 305 free(filename);
304 free(linkname); 306 free(linkname);
305 return err; 307 return err;
@@ -946,11 +948,16 @@ perf_header__find_attr(u64 id, struct perf_header *header)
946 948
947 /* 949 /*
948 * We set id to -1 if the data file doesn't contain sample 950 * We set id to -1 if the data file doesn't contain sample
949 * ids. Check for this and avoid walking through the entire 951 * ids. This can happen when the data file contains one type
950 * list of ids which may be large. 952 * of event and in that case, the header can still store the
953 * event attribute information. Check for this and avoid
954 * walking through the entire list of ids which may be large.
951 */ 955 */
952 if (id == -1ULL) 956 if (id == -1ULL) {
957 if (header->attrs > 0)
958 return &header->attr[0]->attr;
953 return NULL; 959 return NULL;
960 }
954 961
955 for (i = 0; i < header->attrs; i++) { 962 for (i = 0; i < header->attrs; i++) {
956 struct perf_header_attr *attr = header->attr[i]; 963 struct perf_header_attr *attr = header->attr[i];
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index e7263d49bcf0..2022e8740994 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -87,7 +87,7 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self,
87 87
88static struct hist_entry *hist_entry__new(struct hist_entry *template) 88static struct hist_entry *hist_entry__new(struct hist_entry *template)
89{ 89{
90 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_node) : 0; 90 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
91 struct hist_entry *self = malloc(sizeof(*self) + callchain_size); 91 struct hist_entry *self = malloc(sizeof(*self) + callchain_size);
92 92
93 if (self != NULL) { 93 if (self != NULL) {
@@ -226,6 +226,8 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he)
226 226
227 if (!cmp) { 227 if (!cmp) {
228 iter->period += he->period; 228 iter->period += he->period;
229 if (symbol_conf.use_callchain)
230 callchain_merge(iter->callchain, he->callchain);
229 hist_entry__free(he); 231 hist_entry__free(he);
230 return false; 232 return false;
231 } 233 }
@@ -876,6 +878,9 @@ unsigned int hists__sort_list_width(struct hists *self)
876 if (!se->elide) 878 if (!se->elide)
877 ret += 2 + hists__col_len(self, se->se_width_idx); 879 ret += 2 + hists__col_len(self, se->se_width_idx);
878 880
881 if (verbose) /* Addr + origin */
882 ret += 3 + BITS_PER_LONG / 4;
883
879 return ret; 884 return ret;
880} 885}
881 886
@@ -980,9 +985,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
980 return 0; 985 return 0;
981} 986}
982 987
983static struct objdump_line *objdump_line__new(s64 offset, char *line) 988static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
984{ 989{
985 struct objdump_line *self = malloc(sizeof(*self)); 990 struct objdump_line *self = malloc(sizeof(*self) + privsize);
986 991
987 if (self != NULL) { 992 if (self != NULL) {
988 self->offset = offset; 993 self->offset = offset;
@@ -1014,7 +1019,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
1014} 1019}
1015 1020
1016static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1021static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1017 struct list_head *head) 1022 struct list_head *head, size_t privsize)
1018{ 1023{
1019 struct symbol *sym = self->ms.sym; 1024 struct symbol *sym = self->ms.sym;
1020 struct objdump_line *objdump_line; 1025 struct objdump_line *objdump_line;
@@ -1065,7 +1070,7 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1065 offset = -1; 1070 offset = -1;
1066 } 1071 }
1067 1072
1068 objdump_line = objdump_line__new(offset, line); 1073 objdump_line = objdump_line__new(offset, line, privsize);
1069 if (objdump_line == NULL) { 1074 if (objdump_line == NULL) {
1070 free(line); 1075 free(line);
1071 return -1; 1076 return -1;
@@ -1075,7 +1080,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
1075 return 0; 1080 return 0;
1076} 1081}
1077 1082
1078int hist_entry__annotate(struct hist_entry *self, struct list_head *head) 1083int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1084 size_t privsize)
1079{ 1085{
1080 struct symbol *sym = self->ms.sym; 1086 struct symbol *sym = self->ms.sym;
1081 struct map *map = self->ms.map; 1087 struct map *map = self->ms.map;
@@ -1140,7 +1146,7 @@ fallback:
1140 goto out_free_filename; 1146 goto out_free_filename;
1141 1147
1142 while (!feof(file)) 1148 while (!feof(file))
1143 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1149 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1144 break; 1150 break;
1145 1151
1146 pclose(file); 1152 pclose(file);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 65a48db46a29..587d375d3430 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -101,7 +101,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
101 bool show_displacement, FILE *fp); 101 bool show_displacement, FILE *fp);
102 102
103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); 103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
104int hist_entry__annotate(struct hist_entry *self, struct list_head *head); 104int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
105 size_t privsize);
105 106
106void hists__filter_by_dso(struct hists *self, const struct dso *dso); 107void hists__filter_by_dso(struct hists *self, const struct dso *dso);
107void hists__filter_by_thread(struct hists *self, const struct thread *thread); 108void hists__filter_by_thread(struct hists *self, const struct thread *thread);
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index dbe4b814382a..f5ca26e53fbb 100644
--- a/tools/perf/util/include/linux/list.h
+++ b/tools/perf/util/include/linux/list.h
@@ -15,4 +15,12 @@ static inline void list_del_range(struct list_head *begin,
15 begin->prev->next = end->next; 15 begin->prev->next = end->next;
16 end->next->prev = begin->prev; 16 end->next->prev = begin->prev;
17} 17}
18
19/**
20 * list_for_each_from - iterate over a list from one of its nodes
21 * @pos: the &struct list_head to use as a loop cursor, from where to start
22 * @head: the head for your list.
23 */
24#define list_for_each_from(pos, head) \
25 for (; prefetch(pos->next), pos != (head); pos = pos->next)
18#endif 26#endif
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
index 196862a81a21..12de3b8112f9 100644
--- a/tools/perf/util/include/linux/types.h
+++ b/tools/perf/util/include/linux/types.h
@@ -6,4 +6,16 @@
6#define DECLARE_BITMAP(name,bits) \ 6#define DECLARE_BITMAP(name,bits) \
7 unsigned long name[BITS_TO_LONGS(bits)] 7 unsigned long name[BITS_TO_LONGS(bits)]
8 8
9struct list_head {
10 struct list_head *next, *prev;
11};
12
13struct hlist_head {
14 struct hlist_node *first;
15};
16
17struct hlist_node {
18 struct hlist_node *next, **pprev;
19};
20
9#endif 21#endif
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 78575796d5f3..b397c0383728 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self,
215 return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); 215 return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
216} 216}
217 217
218static inline
219struct symbol *machine__find_kernel_function_by_name(struct machine *self,
220 const char *name,
221 struct map **mapp,
222 symbol_filter_t filter)
223{
224 return map_groups__find_function_by_name(&self->kmaps, name, mapp,
225 filter);
226}
227
218int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, 228int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
219 int verbose, FILE *fp); 229 int verbose, FILE *fp);
220 230
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
deleted file mode 100644
index 91de99b58445..000000000000
--- a/tools/perf/util/newt.c
+++ /dev/null
@@ -1,1568 +0,0 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <signal.h>
15#include <stdlib.h>
16#include <newt.h>
17#include <sys/ttydefaults.h>
18
19#include "cache.h"
20#include "hist.h"
21#include "pstack.h"
22#include "session.h"
23#include "sort.h"
24#include "symbol.h"
25
26#if SLANG_VERSION < 20104
27#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
28#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
29#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
30 (char *)fg, (char *)bg)
31#else
32#define slsmg_printf SLsmg_printf
33#define slsmg_write_nstring SLsmg_write_nstring
34#define sltt_set_color SLtt_set_color
35#endif
36
37struct ui_progress {
38 newtComponent form, scale;
39};
40
41struct ui_progress *ui_progress__new(const char *title, u64 total)
42{
43 struct ui_progress *self = malloc(sizeof(*self));
44
45 if (self != NULL) {
46 int cols;
47
48 if (use_browser <= 0)
49 return self;
50 newtGetScreenSize(&cols, NULL);
51 cols -= 4;
52 newtCenteredWindow(cols, 1, title);
53 self->form = newtForm(NULL, NULL, 0);
54 if (self->form == NULL)
55 goto out_free_self;
56 self->scale = newtScale(0, 0, cols, total);
57 if (self->scale == NULL)
58 goto out_free_form;
59 newtFormAddComponent(self->form, self->scale);
60 newtRefresh();
61 }
62
63 return self;
64
65out_free_form:
66 newtFormDestroy(self->form);
67out_free_self:
68 free(self);
69 return NULL;
70}
71
72void ui_progress__update(struct ui_progress *self, u64 curr)
73{
74 /*
75 * FIXME: We should have a per UI backend way of showing progress,
76 * stdio will just show a percentage as NN%, etc.
77 */
78 if (use_browser <= 0)
79 return;
80 newtScaleSet(self->scale, curr);
81 newtRefresh();
82}
83
84void ui_progress__delete(struct ui_progress *self)
85{
86 if (use_browser > 0) {
87 newtFormDestroy(self->form);
88 newtPopWindow();
89 }
90 free(self);
91}
92
93static void ui_helpline__pop(void)
94{
95 newtPopHelpLine();
96}
97
98static void ui_helpline__push(const char *msg)
99{
100 newtPushHelpLine(msg);
101}
102
103static void ui_helpline__vpush(const char *fmt, va_list ap)
104{
105 char *s;
106
107 if (vasprintf(&s, fmt, ap) < 0)
108 vfprintf(stderr, fmt, ap);
109 else {
110 ui_helpline__push(s);
111 free(s);
112 }
113}
114
115static void ui_helpline__fpush(const char *fmt, ...)
116{
117 va_list ap;
118
119 va_start(ap, fmt);
120 ui_helpline__vpush(fmt, ap);
121 va_end(ap);
122}
123
124static void ui_helpline__puts(const char *msg)
125{
126 ui_helpline__pop();
127 ui_helpline__push(msg);
128}
129
130static char browser__last_msg[1024];
131
132int browser__show_help(const char *format, va_list ap)
133{
134 int ret;
135 static int backlog;
136
137 ret = vsnprintf(browser__last_msg + backlog,
138 sizeof(browser__last_msg) - backlog, format, ap);
139 backlog += ret;
140
141 if (browser__last_msg[backlog - 1] == '\n') {
142 ui_helpline__puts(browser__last_msg);
143 newtRefresh();
144 backlog = 0;
145 }
146
147 return ret;
148}
149
150static void newt_form__set_exit_keys(newtComponent self)
151{
152 newtFormAddHotKey(self, NEWT_KEY_LEFT);
153 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
154 newtFormAddHotKey(self, 'Q');
155 newtFormAddHotKey(self, 'q');
156 newtFormAddHotKey(self, CTRL('c'));
157}
158
159static newtComponent newt_form__new(void)
160{
161 newtComponent self = newtForm(NULL, NULL, 0);
162 if (self)
163 newt_form__set_exit_keys(self);
164 return self;
165}
166
167static int popup_menu(int argc, char * const argv[])
168{
169 struct newtExitStruct es;
170 int i, rc = -1, max_len = 5;
171 newtComponent listbox, form = newt_form__new();
172
173 if (form == NULL)
174 return -1;
175
176 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
177 if (listbox == NULL)
178 goto out_destroy_form;
179
180 newtFormAddComponent(form, listbox);
181
182 for (i = 0; i < argc; ++i) {
183 int len = strlen(argv[i]);
184 if (len > max_len)
185 max_len = len;
186 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
187 goto out_destroy_form;
188 }
189
190 newtCenteredWindow(max_len, argc, NULL);
191 newtFormRun(form, &es);
192 rc = newtListboxGetCurrent(listbox) - NULL;
193 if (es.reason == NEWT_EXIT_HOTKEY)
194 rc = -1;
195 newtPopWindow();
196out_destroy_form:
197 newtFormDestroy(form);
198 return rc;
199}
200
201static int ui__help_window(const char *text)
202{
203 struct newtExitStruct es;
204 newtComponent tb, form = newt_form__new();
205 int rc = -1;
206 int max_len = 0, nr_lines = 0;
207 const char *t;
208
209 if (form == NULL)
210 return -1;
211
212 t = text;
213 while (1) {
214 const char *sep = strchr(t, '\n');
215 int len;
216
217 if (sep == NULL)
218 sep = strchr(t, '\0');
219 len = sep - t;
220 if (max_len < len)
221 max_len = len;
222 ++nr_lines;
223 if (*sep == '\0')
224 break;
225 t = sep + 1;
226 }
227
228 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
229 if (tb == NULL)
230 goto out_destroy_form;
231
232 newtTextboxSetText(tb, text);
233 newtFormAddComponent(form, tb);
234 newtCenteredWindow(max_len, nr_lines, NULL);
235 newtFormRun(form, &es);
236 newtPopWindow();
237 rc = 0;
238out_destroy_form:
239 newtFormDestroy(form);
240 return rc;
241}
242
243static bool dialog_yesno(const char *msg)
244{
245 /* newtWinChoice should really be accepting const char pointers... */
246 char yes[] = "Yes", no[] = "No";
247 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
248}
249
250static void ui__error_window(const char *fmt, ...)
251{
252 va_list ap;
253
254 va_start(ap, fmt);
255 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
256 va_end(ap);
257}
258
259#define HE_COLORSET_TOP 50
260#define HE_COLORSET_MEDIUM 51
261#define HE_COLORSET_NORMAL 52
262#define HE_COLORSET_SELECTED 53
263#define HE_COLORSET_CODE 54
264
265static int ui_browser__percent_color(double percent, bool current)
266{
267 if (current)
268 return HE_COLORSET_SELECTED;
269 if (percent >= MIN_RED)
270 return HE_COLORSET_TOP;
271 if (percent >= MIN_GREEN)
272 return HE_COLORSET_MEDIUM;
273 return HE_COLORSET_NORMAL;
274}
275
276struct ui_browser {
277 newtComponent form, sb;
278 u64 index, first_visible_entry_idx;
279 void *first_visible_entry, *entries;
280 u16 top, left, width, height;
281 void *priv;
282 unsigned int (*refresh_entries)(struct ui_browser *self);
283 void (*seek)(struct ui_browser *self,
284 off_t offset, int whence);
285 u32 nr_entries;
286};
287
288static void ui_browser__list_head_seek(struct ui_browser *self,
289 off_t offset, int whence)
290{
291 struct list_head *head = self->entries;
292 struct list_head *pos;
293
294 switch (whence) {
295 case SEEK_SET:
296 pos = head->next;
297 break;
298 case SEEK_CUR:
299 pos = self->first_visible_entry;
300 break;
301 case SEEK_END:
302 pos = head->prev;
303 break;
304 default:
305 return;
306 }
307
308 if (offset > 0) {
309 while (offset-- != 0)
310 pos = pos->next;
311 } else {
312 while (offset++ != 0)
313 pos = pos->prev;
314 }
315
316 self->first_visible_entry = pos;
317}
318
319static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
320{
321 return (self->first_visible_entry_idx + row) == self->index;
322}
323
324static void ui_browser__refresh_dimensions(struct ui_browser *self)
325{
326 int cols, rows;
327 newtGetScreenSize(&cols, &rows);
328
329 if (self->width > cols - 4)
330 self->width = cols - 4;
331 self->height = rows - 5;
332 if (self->height > self->nr_entries)
333 self->height = self->nr_entries;
334 self->top = (rows - self->height) / 2;
335 self->left = (cols - self->width) / 2;
336}
337
338static void ui_browser__reset_index(struct ui_browser *self)
339{
340 self->index = self->first_visible_entry_idx = 0;
341 self->seek(self, 0, SEEK_SET);
342}
343
344static int ui_browser__show(struct ui_browser *self, const char *title)
345{
346 if (self->form != NULL) {
347 newtFormDestroy(self->form);
348 newtPopWindow();
349 }
350 ui_browser__refresh_dimensions(self);
351 newtCenteredWindow(self->width, self->height, title);
352 self->form = newt_form__new();
353 if (self->form == NULL)
354 return -1;
355
356 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
357 HE_COLORSET_NORMAL,
358 HE_COLORSET_SELECTED);
359 if (self->sb == NULL)
360 return -1;
361
362 newtFormAddHotKey(self->form, NEWT_KEY_UP);
363 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
364 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
365 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
366 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
367 newtFormAddHotKey(self->form, NEWT_KEY_END);
368 newtFormAddComponent(self->form, self->sb);
369 return 0;
370}
371
372static int objdump_line__show(struct objdump_line *self, struct list_head *head,
373 int width, struct hist_entry *he, int len,
374 bool current_entry)
375{
376 if (self->offset != -1) {
377 struct symbol *sym = he->ms.sym;
378 unsigned int hits = 0;
379 double percent = 0.0;
380 int color;
381 struct sym_priv *priv = symbol__priv(sym);
382 struct sym_ext *sym_ext = priv->ext;
383 struct sym_hist *h = priv->hist;
384 s64 offset = self->offset;
385 struct objdump_line *next = objdump__get_next_ip_line(head, self);
386
387 while (offset < (s64)len &&
388 (next == NULL || offset < next->offset)) {
389 if (sym_ext) {
390 percent += sym_ext[offset].percent;
391 } else
392 hits += h->ip[offset];
393
394 ++offset;
395 }
396
397 if (sym_ext == NULL && h->sum)
398 percent = 100.0 * hits / h->sum;
399
400 color = ui_browser__percent_color(percent, current_entry);
401 SLsmg_set_color(color);
402 slsmg_printf(" %7.2f ", percent);
403 if (!current_entry)
404 SLsmg_set_color(HE_COLORSET_CODE);
405 } else {
406 int color = ui_browser__percent_color(0, current_entry);
407 SLsmg_set_color(color);
408 slsmg_write_nstring(" ", 9);
409 }
410
411 SLsmg_write_char(':');
412 slsmg_write_nstring(" ", 8);
413 if (!*self->line)
414 slsmg_write_nstring(" ", width - 18);
415 else
416 slsmg_write_nstring(self->line, width - 18);
417
418 return 0;
419}
420
421static int ui_browser__refresh_entries(struct ui_browser *self)
422{
423 int row;
424
425 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
426 row = self->refresh_entries(self);
427 SLsmg_set_color(HE_COLORSET_NORMAL);
428 SLsmg_fill_region(self->top + row, self->left,
429 self->height - row, self->width, ' ');
430
431 return 0;
432}
433
434static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
435{
436 if (ui_browser__refresh_entries(self) < 0)
437 return -1;
438
439 while (1) {
440 off_t offset;
441
442 newtFormRun(self->form, es);
443
444 if (es->reason != NEWT_EXIT_HOTKEY)
445 break;
446 if (is_exit_key(es->u.key))
447 return es->u.key;
448 switch (es->u.key) {
449 case NEWT_KEY_DOWN:
450 if (self->index == self->nr_entries - 1)
451 break;
452 ++self->index;
453 if (self->index == self->first_visible_entry_idx + self->height) {
454 ++self->first_visible_entry_idx;
455 self->seek(self, +1, SEEK_CUR);
456 }
457 break;
458 case NEWT_KEY_UP:
459 if (self->index == 0)
460 break;
461 --self->index;
462 if (self->index < self->first_visible_entry_idx) {
463 --self->first_visible_entry_idx;
464 self->seek(self, -1, SEEK_CUR);
465 }
466 break;
467 case NEWT_KEY_PGDN:
468 case ' ':
469 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
470 break;
471
472 offset = self->height;
473 if (self->index + offset > self->nr_entries - 1)
474 offset = self->nr_entries - 1 - self->index;
475 self->index += offset;
476 self->first_visible_entry_idx += offset;
477 self->seek(self, +offset, SEEK_CUR);
478 break;
479 case NEWT_KEY_PGUP:
480 if (self->first_visible_entry_idx == 0)
481 break;
482
483 if (self->first_visible_entry_idx < self->height)
484 offset = self->first_visible_entry_idx;
485 else
486 offset = self->height;
487
488 self->index -= offset;
489 self->first_visible_entry_idx -= offset;
490 self->seek(self, -offset, SEEK_CUR);
491 break;
492 case NEWT_KEY_HOME:
493 ui_browser__reset_index(self);
494 break;
495 case NEWT_KEY_END:
496 offset = self->height - 1;
497 if (offset >= self->nr_entries)
498 offset = self->nr_entries - 1;
499
500 self->index = self->nr_entries - 1;
501 self->first_visible_entry_idx = self->index - offset;
502 self->seek(self, -offset, SEEK_END);
503 break;
504 default:
505 return es->u.key;
506 }
507 if (ui_browser__refresh_entries(self) < 0)
508 return -1;
509 }
510 return 0;
511}
512
513static char *callchain_list__sym_name(struct callchain_list *self,
514 char *bf, size_t bfsize)
515{
516 if (self->ms.sym)
517 return self->ms.sym->name;
518
519 snprintf(bf, bfsize, "%#Lx", self->ip);
520 return bf;
521}
522
523static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self)
524{
525 struct objdump_line *pos;
526 struct list_head *head = self->entries;
527 struct hist_entry *he = self->priv;
528 int row = 0;
529 int len = he->ms.sym->end - he->ms.sym->start;
530
531 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
532 self->first_visible_entry = head->next;
533
534 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
535
536 list_for_each_entry_from(pos, head, node) {
537 bool current_entry = ui_browser__is_current_entry(self, row);
538 SLsmg_gotorc(self->top + row, self->left);
539 objdump_line__show(pos, head, self->width,
540 he, len, current_entry);
541 if (++row == self->height)
542 break;
543 }
544
545 return row;
546}
547
548int hist_entry__tui_annotate(struct hist_entry *self)
549{
550 struct ui_browser browser;
551 struct newtExitStruct es;
552 struct objdump_line *pos, *n;
553 LIST_HEAD(head);
554 int ret;
555
556 if (self->ms.sym == NULL)
557 return -1;
558
559 if (self->ms.map->dso->annotate_warned)
560 return -1;
561
562 if (hist_entry__annotate(self, &head) < 0) {
563 ui__error_window(browser__last_msg);
564 return -1;
565 }
566
567 ui_helpline__push("Press <- or ESC to exit");
568
569 memset(&browser, 0, sizeof(browser));
570 browser.entries = &head;
571 browser.refresh_entries = hist_entry__annotate_browser_refresh;
572 browser.seek = ui_browser__list_head_seek;
573 browser.priv = self;
574 list_for_each_entry(pos, &head, node) {
575 size_t line_len = strlen(pos->line);
576 if (browser.width < line_len)
577 browser.width = line_len;
578 ++browser.nr_entries;
579 }
580
581 browser.width += 18; /* Percentage */
582 ui_browser__show(&browser, self->ms.sym->name);
583 newtFormAddHotKey(browser.form, ' ');
584 ret = ui_browser__run(&browser, &es);
585 newtFormDestroy(browser.form);
586 newtPopWindow();
587 list_for_each_entry_safe(pos, n, &head, node) {
588 list_del(&pos->node);
589 objdump_line__free(pos);
590 }
591 ui_helpline__pop();
592 return ret;
593}
594
595struct hist_browser {
596 struct ui_browser b;
597 struct hists *hists;
598 struct hist_entry *he_selection;
599 struct map_symbol *selection;
600};
601
602static void hist_browser__reset(struct hist_browser *self);
603static int hist_browser__run(struct hist_browser *self, const char *title,
604 struct newtExitStruct *es);
605static unsigned int hist_browser__refresh_entries(struct ui_browser *self);
606static void ui_browser__hists_seek(struct ui_browser *self,
607 off_t offset, int whence);
608
609static struct hist_browser *hist_browser__new(struct hists *hists)
610{
611 struct hist_browser *self = zalloc(sizeof(*self));
612
613 if (self) {
614 self->hists = hists;
615 self->b.refresh_entries = hist_browser__refresh_entries;
616 self->b.seek = ui_browser__hists_seek;
617 }
618
619 return self;
620}
621
622static void hist_browser__delete(struct hist_browser *self)
623{
624 newtFormDestroy(self->b.form);
625 newtPopWindow();
626 free(self);
627}
628
629static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
630{
631 return self->he_selection;
632}
633
634static struct thread *hist_browser__selected_thread(struct hist_browser *self)
635{
636 return self->he_selection->thread;
637}
638
639static int hist_browser__title(char *bf, size_t size, const char *ev_name,
640 const struct dso *dso, const struct thread *thread)
641{
642 int printed = 0;
643
644 if (thread)
645 printed += snprintf(bf + printed, size - printed,
646 "Thread: %s(%d)",
647 (thread->comm_set ? thread->comm : ""),
648 thread->pid);
649 if (dso)
650 printed += snprintf(bf + printed, size - printed,
651 "%sDSO: %s", thread ? " " : "",
652 dso->short_name);
653 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
654}
655
656int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
657{
658 struct hist_browser *browser = hist_browser__new(self);
659 struct pstack *fstack;
660 const struct thread *thread_filter = NULL;
661 const struct dso *dso_filter = NULL;
662 struct newtExitStruct es;
663 char msg[160];
664 int key = -1;
665
666 if (browser == NULL)
667 return -1;
668
669 fstack = pstack__new(2);
670 if (fstack == NULL)
671 goto out;
672
673 ui_helpline__push(helpline);
674
675 hist_browser__title(msg, sizeof(msg), ev_name,
676 dso_filter, thread_filter);
677
678 while (1) {
679 const struct thread *thread;
680 const struct dso *dso;
681 char *options[16];
682 int nr_options = 0, choice = 0, i,
683 annotate = -2, zoom_dso = -2, zoom_thread = -2;
684
685 if (hist_browser__run(browser, msg, &es))
686 break;
687
688 thread = hist_browser__selected_thread(browser);
689 dso = browser->selection->map ? browser->selection->map->dso : NULL;
690
691 if (es.reason == NEWT_EXIT_HOTKEY) {
692 key = es.u.key;
693
694 switch (key) {
695 case NEWT_KEY_F1:
696 goto do_help;
697 case NEWT_KEY_TAB:
698 case NEWT_KEY_UNTAB:
699 /*
700 * Exit the browser, let hists__browser_tree
701 * go to the next or previous
702 */
703 goto out_free_stack;
704 default:;
705 }
706
707 key = toupper(key);
708 switch (key) {
709 case 'A':
710 if (browser->selection->map == NULL &&
711 browser->selection->map->dso->annotate_warned)
712 continue;
713 goto do_annotate;
714 case 'D':
715 goto zoom_dso;
716 case 'T':
717 goto zoom_thread;
718 case 'H':
719 case '?':
720do_help:
721 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
722 "<- Zoom out\n"
723 "a Annotate current symbol\n"
724 "h/?/F1 Show this window\n"
725 "d Zoom into current DSO\n"
726 "t Zoom into current Thread\n"
727 "q/CTRL+C Exit browser");
728 continue;
729 default:;
730 }
731 if (is_exit_key(key)) {
732 if (key == NEWT_KEY_ESCAPE) {
733 if (dialog_yesno("Do you really want to exit?"))
734 break;
735 else
736 continue;
737 } else
738 break;
739 }
740
741 if (es.u.key == NEWT_KEY_LEFT) {
742 const void *top;
743
744 if (pstack__empty(fstack))
745 continue;
746 top = pstack__pop(fstack);
747 if (top == &dso_filter)
748 goto zoom_out_dso;
749 if (top == &thread_filter)
750 goto zoom_out_thread;
751 continue;
752 }
753 }
754
755 if (browser->selection->sym != NULL &&
756 !browser->selection->map->dso->annotate_warned &&
757 asprintf(&options[nr_options], "Annotate %s",
758 browser->selection->sym->name) > 0)
759 annotate = nr_options++;
760
761 if (thread != NULL &&
762 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
763 (thread_filter ? "out of" : "into"),
764 (thread->comm_set ? thread->comm : ""),
765 thread->pid) > 0)
766 zoom_thread = nr_options++;
767
768 if (dso != NULL &&
769 asprintf(&options[nr_options], "Zoom %s %s DSO",
770 (dso_filter ? "out of" : "into"),
771 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
772 zoom_dso = nr_options++;
773
774 options[nr_options++] = (char *)"Exit";
775
776 choice = popup_menu(nr_options, options);
777
778 for (i = 0; i < nr_options - 1; ++i)
779 free(options[i]);
780
781 if (choice == nr_options - 1)
782 break;
783
784 if (choice == -1)
785 continue;
786
787 if (choice == annotate) {
788 struct hist_entry *he;
789do_annotate:
790 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
791 browser->selection->map->dso->annotate_warned = 1;
792 ui_helpline__puts("No vmlinux file found, can't "
793 "annotate with just a "
794 "kallsyms file");
795 continue;
796 }
797
798 he = hist_browser__selected_entry(browser);
799 if (he == NULL)
800 continue;
801
802 hist_entry__tui_annotate(he);
803 } else if (choice == zoom_dso) {
804zoom_dso:
805 if (dso_filter) {
806 pstack__remove(fstack, &dso_filter);
807zoom_out_dso:
808 ui_helpline__pop();
809 dso_filter = NULL;
810 } else {
811 if (dso == NULL)
812 continue;
813 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
814 dso->kernel ? "the Kernel" : dso->short_name);
815 dso_filter = dso;
816 pstack__push(fstack, &dso_filter);
817 }
818 hists__filter_by_dso(self, dso_filter);
819 hist_browser__title(msg, sizeof(msg), ev_name,
820 dso_filter, thread_filter);
821 hist_browser__reset(browser);
822 } else if (choice == zoom_thread) {
823zoom_thread:
824 if (thread_filter) {
825 pstack__remove(fstack, &thread_filter);
826zoom_out_thread:
827 ui_helpline__pop();
828 thread_filter = NULL;
829 } else {
830 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
831 thread->comm_set ? thread->comm : "",
832 thread->pid);
833 thread_filter = thread;
834 pstack__push(fstack, &thread_filter);
835 }
836 hists__filter_by_thread(self, thread_filter);
837 hist_browser__title(msg, sizeof(msg), ev_name,
838 dso_filter, thread_filter);
839 hist_browser__reset(browser);
840 }
841 }
842out_free_stack:
843 pstack__delete(fstack);
844out:
845 hist_browser__delete(browser);
846 return key;
847}
848
849int hists__tui_browse_tree(struct rb_root *self, const char *help)
850{
851 struct rb_node *first = rb_first(self), *nd = first, *next;
852 int key = 0;
853
854 while (nd) {
855 struct hists *hists = rb_entry(nd, struct hists, rb_node);
856 const char *ev_name = __event_name(hists->type, hists->config);
857
858 key = hists__browse(hists, help, ev_name);
859
860 if (is_exit_key(key))
861 break;
862
863 switch (key) {
864 case NEWT_KEY_TAB:
865 next = rb_next(nd);
866 if (next)
867 nd = next;
868 break;
869 case NEWT_KEY_UNTAB:
870 if (nd == first)
871 continue;
872 nd = rb_prev(nd);
873 default:
874 break;
875 }
876 }
877
878 return key;
879}
880
881static struct newtPercentTreeColors {
882 const char *topColorFg, *topColorBg;
883 const char *mediumColorFg, *mediumColorBg;
884 const char *normalColorFg, *normalColorBg;
885 const char *selColorFg, *selColorBg;
886 const char *codeColorFg, *codeColorBg;
887} defaultPercentTreeColors = {
888 "red", "lightgray",
889 "green", "lightgray",
890 "black", "lightgray",
891 "lightgray", "magenta",
892 "blue", "lightgray",
893};
894
895static void newt_suspend(void *d __used)
896{
897 newtSuspend();
898 raise(SIGTSTP);
899 newtResume();
900}
901
902void setup_browser(void)
903{
904 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
905
906 if (!isatty(1) || !use_browser || dump_trace) {
907 use_browser = 0;
908 setup_pager();
909 return;
910 }
911
912 use_browser = 1;
913 newtInit();
914 newtCls();
915 newtSetSuspendCallback(newt_suspend, NULL);
916 ui_helpline__puts(" ");
917 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
918 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
919 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
920 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
921 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
922}
923
924void exit_browser(bool wait_for_ok)
925{
926 if (use_browser > 0) {
927 if (wait_for_ok) {
928 char title[] = "Fatal Error", ok[] = "Ok";
929 newtWinMessage(title, ok, browser__last_msg);
930 }
931 newtFinished();
932 }
933}
934
935static void hist_browser__refresh_dimensions(struct hist_browser *self)
936{
937 /* 3 == +/- toggle symbol before actual hist_entry rendering */
938 self->b.width = 3 + (hists__sort_list_width(self->hists) +
939 sizeof("[k]"));
940}
941
942static void hist_browser__reset(struct hist_browser *self)
943{
944 self->b.nr_entries = self->hists->nr_entries;
945 hist_browser__refresh_dimensions(self);
946 ui_browser__reset_index(&self->b);
947}
948
949static char tree__folded_sign(bool unfolded)
950{
951 return unfolded ? '-' : '+';
952}
953
954static char map_symbol__folded(const struct map_symbol *self)
955{
956 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
957}
958
959static char hist_entry__folded(const struct hist_entry *self)
960{
961 return map_symbol__folded(&self->ms);
962}
963
964static char callchain_list__folded(const struct callchain_list *self)
965{
966 return map_symbol__folded(&self->ms);
967}
968
969static bool map_symbol__toggle_fold(struct map_symbol *self)
970{
971 if (!self->has_children)
972 return false;
973
974 self->unfolded = !self->unfolded;
975 return true;
976}
977
978#define LEVEL_OFFSET_STEP 3
979
980static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
981 struct callchain_node *chain_node,
982 u64 total, int level,
983 unsigned short row,
984 off_t *row_offset,
985 bool *is_current_entry)
986{
987 struct rb_node *node;
988 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
989 u64 new_total, remaining;
990
991 if (callchain_param.mode == CHAIN_GRAPH_REL)
992 new_total = chain_node->children_hit;
993 else
994 new_total = total;
995
996 remaining = new_total;
997 node = rb_first(&chain_node->rb_root);
998 while (node) {
999 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1000 struct rb_node *next = rb_next(node);
1001 u64 cumul = cumul_hits(child);
1002 struct callchain_list *chain;
1003 char folded_sign = ' ';
1004 int first = true;
1005 int extra_offset = 0;
1006
1007 remaining -= cumul;
1008
1009 list_for_each_entry(chain, &child->val, list) {
1010 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
1011 const char *str;
1012 int color;
1013 bool was_first = first;
1014
1015 if (first) {
1016 first = false;
1017 chain->ms.has_children = chain->list.next != &child->val ||
1018 rb_first(&child->rb_root) != NULL;
1019 } else {
1020 extra_offset = LEVEL_OFFSET_STEP;
1021 chain->ms.has_children = chain->list.next == &child->val &&
1022 rb_first(&child->rb_root) != NULL;
1023 }
1024
1025 folded_sign = callchain_list__folded(chain);
1026 if (*row_offset != 0) {
1027 --*row_offset;
1028 goto do_next;
1029 }
1030
1031 alloc_str = NULL;
1032 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1033 if (was_first) {
1034 double percent = cumul * 100.0 / new_total;
1035
1036 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
1037 str = "Not enough memory!";
1038 else
1039 str = alloc_str;
1040 }
1041
1042 color = HE_COLORSET_NORMAL;
1043 width = self->b.width - (offset + extra_offset + 2);
1044 if (ui_browser__is_current_entry(&self->b, row)) {
1045 self->selection = &chain->ms;
1046 color = HE_COLORSET_SELECTED;
1047 *is_current_entry = true;
1048 }
1049
1050 SLsmg_set_color(color);
1051 SLsmg_gotorc(self->b.top + row, self->b.left);
1052 slsmg_write_nstring(" ", offset + extra_offset);
1053 slsmg_printf("%c ", folded_sign);
1054 slsmg_write_nstring(str, width);
1055 free(alloc_str);
1056
1057 if (++row == self->b.height)
1058 goto out;
1059do_next:
1060 if (folded_sign == '+')
1061 break;
1062 }
1063
1064 if (folded_sign == '-') {
1065 const int new_level = level + (extra_offset ? 2 : 1);
1066 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
1067 new_level, row, row_offset,
1068 is_current_entry);
1069 }
1070 if (row == self->b.height)
1071 goto out;
1072 node = next;
1073 }
1074out:
1075 return row - first_row;
1076}
1077
1078static int hist_browser__show_callchain_node(struct hist_browser *self,
1079 struct callchain_node *node,
1080 int level, unsigned short row,
1081 off_t *row_offset,
1082 bool *is_current_entry)
1083{
1084 struct callchain_list *chain;
1085 int first_row = row,
1086 offset = level * LEVEL_OFFSET_STEP,
1087 width = self->b.width - offset;
1088 char folded_sign = ' ';
1089
1090 list_for_each_entry(chain, &node->val, list) {
1091 char ipstr[BITS_PER_LONG / 4 + 1], *s;
1092 int color;
1093 /*
1094 * FIXME: This should be moved to somewhere else,
1095 * probably when the callchain is created, so as not to
1096 * traverse it all over again
1097 */
1098 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
1099 folded_sign = callchain_list__folded(chain);
1100
1101 if (*row_offset != 0) {
1102 --*row_offset;
1103 continue;
1104 }
1105
1106 color = HE_COLORSET_NORMAL;
1107 if (ui_browser__is_current_entry(&self->b, row)) {
1108 self->selection = &chain->ms;
1109 color = HE_COLORSET_SELECTED;
1110 *is_current_entry = true;
1111 }
1112
1113 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
1114 SLsmg_gotorc(self->b.top + row, self->b.left);
1115 SLsmg_set_color(color);
1116 slsmg_write_nstring(" ", offset);
1117 slsmg_printf("%c ", folded_sign);
1118 slsmg_write_nstring(s, width - 2);
1119
1120 if (++row == self->b.height)
1121 goto out;
1122 }
1123
1124 if (folded_sign == '-')
1125 row += hist_browser__show_callchain_node_rb_tree(self, node,
1126 self->hists->stats.total_period,
1127 level + 1, row,
1128 row_offset,
1129 is_current_entry);
1130out:
1131 return row - first_row;
1132}
1133
1134static int hist_browser__show_callchain(struct hist_browser *self,
1135 struct rb_root *chain,
1136 int level, unsigned short row,
1137 off_t *row_offset,
1138 bool *is_current_entry)
1139{
1140 struct rb_node *nd;
1141 int first_row = row;
1142
1143 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1144 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1145
1146 row += hist_browser__show_callchain_node(self, node, level,
1147 row, row_offset,
1148 is_current_entry);
1149 if (row == self->b.height)
1150 break;
1151 }
1152
1153 return row - first_row;
1154}
1155
1156static int hist_browser__show_entry(struct hist_browser *self,
1157 struct hist_entry *entry,
1158 unsigned short row)
1159{
1160 char s[256];
1161 double percent;
1162 int printed = 0;
1163 int color, width = self->b.width;
1164 char folded_sign = ' ';
1165 bool current_entry = ui_browser__is_current_entry(&self->b, row);
1166 off_t row_offset = entry->row_offset;
1167
1168 if (current_entry) {
1169 self->he_selection = entry;
1170 self->selection = &entry->ms;
1171 }
1172
1173 if (symbol_conf.use_callchain) {
1174 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
1175 folded_sign = hist_entry__folded(entry);
1176 }
1177
1178 if (row_offset == 0) {
1179 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
1180 0, false, self->hists->stats.total_period);
1181 percent = (entry->period * 100.0) / self->hists->stats.total_period;
1182
1183 color = HE_COLORSET_SELECTED;
1184 if (!current_entry) {
1185 if (percent >= MIN_RED)
1186 color = HE_COLORSET_TOP;
1187 else if (percent >= MIN_GREEN)
1188 color = HE_COLORSET_MEDIUM;
1189 else
1190 color = HE_COLORSET_NORMAL;
1191 }
1192
1193 SLsmg_set_color(color);
1194 SLsmg_gotorc(self->b.top + row, self->b.left);
1195 if (symbol_conf.use_callchain) {
1196 slsmg_printf("%c ", folded_sign);
1197 width -= 2;
1198 }
1199 slsmg_write_nstring(s, width);
1200 ++row;
1201 ++printed;
1202 } else
1203 --row_offset;
1204
1205 if (folded_sign == '-' && row != self->b.height) {
1206 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
1207 1, row, &row_offset,
1208 &current_entry);
1209 if (current_entry)
1210 self->he_selection = entry;
1211 }
1212
1213 return printed;
1214}
1215
1216static unsigned int hist_browser__refresh_entries(struct ui_browser *self)
1217{
1218 unsigned row = 0;
1219 struct rb_node *nd;
1220 struct hist_browser *hb = container_of(self, struct hist_browser, b);
1221
1222 if (self->first_visible_entry == NULL)
1223 self->first_visible_entry = rb_first(&hb->hists->entries);
1224
1225 for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) {
1226 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1227
1228 if (h->filtered)
1229 continue;
1230
1231 row += hist_browser__show_entry(hb, h, row);
1232 if (row == self->height)
1233 break;
1234 }
1235
1236 return row;
1237}
1238
1239static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
1240{
1241 struct rb_node *nd = rb_first(&self->rb_root);
1242
1243 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1244 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1245 struct callchain_list *chain;
1246 int first = true;
1247
1248 list_for_each_entry(chain, &child->val, list) {
1249 if (first) {
1250 first = false;
1251 chain->ms.has_children = chain->list.next != &child->val ||
1252 rb_first(&child->rb_root) != NULL;
1253 } else
1254 chain->ms.has_children = chain->list.next == &child->val &&
1255 rb_first(&child->rb_root) != NULL;
1256 }
1257
1258 callchain_node__init_have_children_rb_tree(child);
1259 }
1260}
1261
1262static void callchain_node__init_have_children(struct callchain_node *self)
1263{
1264 struct callchain_list *chain;
1265
1266 list_for_each_entry(chain, &self->val, list)
1267 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
1268
1269 callchain_node__init_have_children_rb_tree(self);
1270}
1271
1272static void callchain__init_have_children(struct rb_root *self)
1273{
1274 struct rb_node *nd;
1275
1276 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
1277 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1278 callchain_node__init_have_children(node);
1279 }
1280}
1281
1282static void hist_entry__init_have_children(struct hist_entry *self)
1283{
1284 if (!self->init_have_children) {
1285 callchain__init_have_children(&self->sorted_chain);
1286 self->init_have_children = true;
1287 }
1288}
1289
1290static struct rb_node *hists__filter_entries(struct rb_node *nd)
1291{
1292 while (nd != NULL) {
1293 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1294 if (!h->filtered)
1295 return nd;
1296
1297 nd = rb_next(nd);
1298 }
1299
1300 return NULL;
1301}
1302
1303static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
1304{
1305 while (nd != NULL) {
1306 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1307 if (!h->filtered)
1308 return nd;
1309
1310 nd = rb_prev(nd);
1311 }
1312
1313 return NULL;
1314}
1315
1316static void ui_browser__hists_seek(struct ui_browser *self,
1317 off_t offset, int whence)
1318{
1319 struct hist_entry *h;
1320 struct rb_node *nd;
1321 bool first = true;
1322
1323 switch (whence) {
1324 case SEEK_SET:
1325 nd = hists__filter_entries(rb_first(self->entries));
1326 break;
1327 case SEEK_CUR:
1328 nd = self->first_visible_entry;
1329 goto do_offset;
1330 case SEEK_END:
1331 nd = hists__filter_prev_entries(rb_last(self->entries));
1332 first = false;
1333 break;
1334 default:
1335 return;
1336 }
1337
1338 /*
1339 * Moves not relative to the first visible entry invalidates its
1340 * row_offset:
1341 */
1342 h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node);
1343 h->row_offset = 0;
1344
1345 /*
1346 * Here we have to check if nd is expanded (+), if it is we can't go
1347 * the next top level hist_entry, instead we must compute an offset of
1348 * what _not_ to show and not change the first visible entry.
1349 *
1350 * This offset increments when we are going from top to bottom and
1351 * decreases when we're going from bottom to top.
1352 *
1353 * As we don't have backpointers to the top level in the callchains
1354 * structure, we need to always print the whole hist_entry callchain,
1355 * skipping the first ones that are before the first visible entry
1356 * and stop when we printed enough lines to fill the screen.
1357 */
1358do_offset:
1359 if (offset > 0) {
1360 do {
1361 h = rb_entry(nd, struct hist_entry, rb_node);
1362 if (h->ms.unfolded) {
1363 u16 remaining = h->nr_rows - h->row_offset;
1364 if (offset > remaining) {
1365 offset -= remaining;
1366 h->row_offset = 0;
1367 } else {
1368 h->row_offset += offset;
1369 offset = 0;
1370 self->first_visible_entry = nd;
1371 break;
1372 }
1373 }
1374 nd = hists__filter_entries(rb_next(nd));
1375 if (nd == NULL)
1376 break;
1377 --offset;
1378 self->first_visible_entry = nd;
1379 } while (offset != 0);
1380 } else if (offset < 0) {
1381 while (1) {
1382 h = rb_entry(nd, struct hist_entry, rb_node);
1383 if (h->ms.unfolded) {
1384 if (first) {
1385 if (-offset > h->row_offset) {
1386 offset += h->row_offset;
1387 h->row_offset = 0;
1388 } else {
1389 h->row_offset += offset;
1390 offset = 0;
1391 self->first_visible_entry = nd;
1392 break;
1393 }
1394 } else {
1395 if (-offset > h->nr_rows) {
1396 offset += h->nr_rows;
1397 h->row_offset = 0;
1398 } else {
1399 h->row_offset = h->nr_rows + offset;
1400 offset = 0;
1401 self->first_visible_entry = nd;
1402 break;
1403 }
1404 }
1405 }
1406
1407 nd = hists__filter_prev_entries(rb_prev(nd));
1408 if (nd == NULL)
1409 break;
1410 ++offset;
1411 self->first_visible_entry = nd;
1412 if (offset == 0) {
1413 /*
1414 * Last unfiltered hist_entry, check if it is
1415 * unfolded, if it is then we should have
1416 * row_offset at its last entry.
1417 */
1418 h = rb_entry(nd, struct hist_entry, rb_node);
1419 if (h->ms.unfolded)
1420 h->row_offset = h->nr_rows;
1421 break;
1422 }
1423 first = false;
1424 }
1425 } else {
1426 self->first_visible_entry = nd;
1427 h = rb_entry(nd, struct hist_entry, rb_node);
1428 h->row_offset = 0;
1429 }
1430}
1431
1432static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
1433{
1434 int n = 0;
1435 struct rb_node *nd;
1436
1437 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
1438 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
1439 struct callchain_list *chain;
1440 char folded_sign = ' '; /* No children */
1441
1442 list_for_each_entry(chain, &child->val, list) {
1443 ++n;
1444 /* We need this because we may not have children */
1445 folded_sign = callchain_list__folded(chain);
1446 if (folded_sign == '+')
1447 break;
1448 }
1449
1450 if (folded_sign == '-') /* Have children and they're unfolded */
1451 n += callchain_node__count_rows_rb_tree(child);
1452 }
1453
1454 return n;
1455}
1456
1457static int callchain_node__count_rows(struct callchain_node *node)
1458{
1459 struct callchain_list *chain;
1460 bool unfolded = false;
1461 int n = 0;
1462
1463 list_for_each_entry(chain, &node->val, list) {
1464 ++n;
1465 unfolded = chain->ms.unfolded;
1466 }
1467
1468 if (unfolded)
1469 n += callchain_node__count_rows_rb_tree(node);
1470
1471 return n;
1472}
1473
1474static int callchain__count_rows(struct rb_root *chain)
1475{
1476 struct rb_node *nd;
1477 int n = 0;
1478
1479 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
1480 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
1481 n += callchain_node__count_rows(node);
1482 }
1483
1484 return n;
1485}
1486
1487static bool hist_browser__toggle_fold(struct hist_browser *self)
1488{
1489 if (map_symbol__toggle_fold(self->selection)) {
1490 struct hist_entry *he = self->he_selection;
1491
1492 hist_entry__init_have_children(he);
1493 self->hists->nr_entries -= he->nr_rows;
1494
1495 if (he->ms.unfolded)
1496 he->nr_rows = callchain__count_rows(&he->sorted_chain);
1497 else
1498 he->nr_rows = 0;
1499 self->hists->nr_entries += he->nr_rows;
1500 self->b.nr_entries = self->hists->nr_entries;
1501
1502 return true;
1503 }
1504
1505 /* If it doesn't have children, no toggling performed */
1506 return false;
1507}
1508
1509static int hist_browser__run(struct hist_browser *self, const char *title,
1510 struct newtExitStruct *es)
1511{
1512 char str[256], unit;
1513 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
1514
1515 self->b.entries = &self->hists->entries;
1516 self->b.nr_entries = self->hists->nr_entries;
1517
1518 hist_browser__refresh_dimensions(self);
1519
1520 nr_events = convert_unit(nr_events, &unit);
1521 snprintf(str, sizeof(str), "Events: %lu%c ",
1522 nr_events, unit);
1523 newtDrawRootText(0, 0, str);
1524
1525 if (ui_browser__show(&self->b, title) < 0)
1526 return -1;
1527
1528 newtFormAddHotKey(self->b.form, 'A');
1529 newtFormAddHotKey(self->b.form, 'a');
1530 newtFormAddHotKey(self->b.form, '?');
1531 newtFormAddHotKey(self->b.form, 'h');
1532 newtFormAddHotKey(self->b.form, 'H');
1533 newtFormAddHotKey(self->b.form, 'd');
1534
1535 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
1536 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
1537 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
1538
1539 while (1) {
1540 ui_browser__run(&self->b, es);
1541
1542 if (es->reason != NEWT_EXIT_HOTKEY)
1543 break;
1544 switch (es->u.key) {
1545 case 'd': { /* Debug */
1546 static int seq;
1547 struct hist_entry *h = rb_entry(self->b.first_visible_entry,
1548 struct hist_entry, rb_node);
1549 ui_helpline__pop();
1550 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
1551 seq++, self->b.nr_entries,
1552 self->hists->nr_entries,
1553 self->b.height,
1554 self->b.index,
1555 self->b.first_visible_entry_idx,
1556 h->row_offset, h->nr_rows);
1557 }
1558 continue;
1559 case NEWT_KEY_ENTER:
1560 if (hist_browser__toggle_fold(self))
1561 break;
1562 /* fall thru */
1563 default:
1564 return 0;
1565 }
1566 }
1567 return 0;
1568}
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c
index 58a470d036dd..bd7497711424 100644
--- a/tools/perf/util/path.c
+++ b/tools/perf/util/path.c
@@ -22,6 +22,7 @@ static const char *get_perf_dir(void)
22 return "."; 22 return ".";
23} 23}
24 24
25#ifdef NO_STRLCPY
25size_t strlcpy(char *dest, const char *src, size_t size) 26size_t strlcpy(char *dest, const char *src, size_t size)
26{ 27{
27 size_t ret = strlen(src); 28 size_t ret = strlen(src);
@@ -33,7 +34,7 @@ size_t strlcpy(char *dest, const char *src, size_t size)
33 } 34 }
34 return ret; 35 return ret;
35} 36}
36 37#endif
37 38
38static char *get_pathname(void) 39static char *get_pathname(void)
39{ 40{
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2e665cb84055..61191c6cbe7a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
74static char *synthesize_perf_probe_point(struct perf_probe_point *pp); 74static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
75static struct machine machine; 75static struct machine machine;
76 76
77/* Initialize symbol maps and path of vmlinux */ 77/* Initialize symbol maps and path of vmlinux/modules */
78static int init_vmlinux(void) 78static int init_vmlinux(void)
79{ 79{
80 struct dso *kernel;
81 int ret; 80 int ret;
82 81
83 symbol_conf.sort_by_name = true; 82 symbol_conf.sort_by_name = true;
@@ -91,33 +90,70 @@ static int init_vmlinux(void)
91 goto out; 90 goto out;
92 } 91 }
93 92
94 ret = machine__init(&machine, "/", 0); 93 ret = machine__init(&machine, "", HOST_KERNEL_ID);
95 if (ret < 0) 94 if (ret < 0)
96 goto out; 95 goto out;
97 96
98 kernel = dso__new_kernel(symbol_conf.vmlinux_name); 97 if (machine__create_kernel_maps(&machine) < 0) {
99 if (kernel == NULL) 98 pr_debug("machine__create_kernel_maps ");
100 die("Failed to create kernel dso."); 99 goto out;
101 100 }
102 ret = __machine__create_kernel_maps(&machine, kernel);
103 if (ret < 0)
104 pr_debug("Failed to create kernel maps.\n");
105
106out: 101out:
107 if (ret < 0) 102 if (ret < 0)
108 pr_warning("Failed to init vmlinux path.\n"); 103 pr_warning("Failed to init vmlinux path.\n");
109 return ret; 104 return ret;
110} 105}
111 106
107static struct symbol *__find_kernel_function_by_name(const char *name,
108 struct map **mapp)
109{
110 return machine__find_kernel_function_by_name(&machine, name, mapp,
111 NULL);
112}
113
114const char *kernel_get_module_path(const char *module)
115{
116 struct dso *dso;
117 struct map *map;
118 const char *vmlinux_name;
119
120 if (module) {
121 list_for_each_entry(dso, &machine.kernel_dsos, node) {
122 if (strncmp(dso->short_name + 1, module,
123 dso->short_name_len - 2) == 0)
124 goto found;
125 }
126 pr_debug("Failed to find module %s.\n", module);
127 return NULL;
128 }
129
130 map = machine.vmlinux_maps[MAP__FUNCTION];
131 dso = map->dso;
132
133 vmlinux_name = symbol_conf.vmlinux_name;
134 if (vmlinux_name) {
135 if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0)
136 return NULL;
137 } else {
138 if (dso__load_vmlinux_path(dso, map, NULL) <= 0) {
139 pr_debug("Failed to load kernel map.\n");
140 return NULL;
141 }
142 }
143found:
144 return dso->long_name;
145}
146
112#ifdef DWARF_SUPPORT 147#ifdef DWARF_SUPPORT
113static int open_vmlinux(void) 148static int open_vmlinux(const char *module)
114{ 149{
115 if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { 150 const char *path = kernel_get_module_path(module);
116 pr_debug("Failed to load kernel map.\n"); 151 if (!path) {
117 return -EINVAL; 152 pr_err("Failed to find path of %s module", module ?: "kernel");
153 return -ENOENT;
118 } 154 }
119 pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); 155 pr_debug("Try to open %s\n", path);
120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); 156 return open(path, O_RDONLY);
121} 157}
122 158
123/* 159/*
@@ -125,20 +161,19 @@ static int open_vmlinux(void)
125 * Currently only handles kprobes. 161 * Currently only handles kprobes.
126 */ 162 */
127static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 163static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
128 struct perf_probe_point *pp) 164 struct perf_probe_point *pp)
129{ 165{
130 struct symbol *sym; 166 struct symbol *sym;
131 int fd, ret = -ENOENT; 167 struct map *map;
168 u64 addr;
169 int ret = -ENOENT;
132 170
133 sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], 171 sym = __find_kernel_function_by_name(tp->symbol, &map);
134 tp->symbol, NULL);
135 if (sym) { 172 if (sym) {
136 fd = open_vmlinux(); 173 addr = map->unmap_ip(map, sym->start + tp->offset);
137 if (fd >= 0) { 174 pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
138 ret = find_perf_probe_point(fd, 175 tp->offset, addr);
139 sym->start + tp->offset, pp); 176 ret = find_perf_probe_point((unsigned long)addr, pp);
140 close(fd);
141 }
142 } 177 }
143 if (ret <= 0) { 178 if (ret <= 0) {
144 pr_debug("Failed to find corresponding probes from " 179 pr_debug("Failed to find corresponding probes from "
@@ -156,12 +191,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
156/* Try to find perf_probe_event with debuginfo */ 191/* Try to find perf_probe_event with debuginfo */
157static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 192static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
158 struct probe_trace_event **tevs, 193 struct probe_trace_event **tevs,
159 int max_tevs) 194 int max_tevs, const char *module)
160{ 195{
161 bool need_dwarf = perf_probe_event_need_dwarf(pev); 196 bool need_dwarf = perf_probe_event_need_dwarf(pev);
162 int fd, ntevs; 197 int fd, ntevs;
163 198
164 fd = open_vmlinux(); 199 fd = open_vmlinux(module);
165 if (fd < 0) { 200 if (fd < 0) {
166 if (need_dwarf) { 201 if (need_dwarf) {
167 pr_warning("Failed to open debuginfo file.\n"); 202 pr_warning("Failed to open debuginfo file.\n");
@@ -300,7 +335,7 @@ error:
300 * Show line-range always requires debuginfo to find source file and 335 * Show line-range always requires debuginfo to find source file and
301 * line number. 336 * line number.
302 */ 337 */
303int show_line_range(struct line_range *lr) 338int show_line_range(struct line_range *lr, const char *module)
304{ 339{
305 int l = 1; 340 int l = 1;
306 struct line_node *ln; 341 struct line_node *ln;
@@ -313,7 +348,7 @@ int show_line_range(struct line_range *lr)
313 if (ret < 0) 348 if (ret < 0)
314 return ret; 349 return ret;
315 350
316 fd = open_vmlinux(); 351 fd = open_vmlinux(module);
317 if (fd < 0) { 352 if (fd < 0) {
318 pr_warning("Failed to open debuginfo file.\n"); 353 pr_warning("Failed to open debuginfo file.\n");
319 return fd; 354 return fd;
@@ -378,11 +413,84 @@ end:
378 return ret; 413 return ret;
379} 414}
380 415
416static int show_available_vars_at(int fd, struct perf_probe_event *pev,
417 int max_vls, bool externs)
418{
419 char *buf;
420 int ret, i;
421 struct str_node *node;
422 struct variable_list *vls = NULL, *vl;
423
424 buf = synthesize_perf_probe_point(&pev->point);
425 if (!buf)
426 return -EINVAL;
427 pr_debug("Searching variables at %s\n", buf);
428
429 ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
430 if (ret > 0) {
431 /* Some variables were found */
432 fprintf(stdout, "Available variables at %s\n", buf);
433 for (i = 0; i < ret; i++) {
434 vl = &vls[i];
435 /*
436 * A probe point might be converted to
437 * several trace points.
438 */
439 fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
440 vl->point.offset);
441 free(vl->point.symbol);
442 if (vl->vars) {
443 strlist__for_each(node, vl->vars)
444 fprintf(stdout, "\t\t%s\n", node->s);
445 strlist__delete(vl->vars);
446 } else
447 fprintf(stdout, "(No variables)\n");
448 }
449 free(vls);
450 } else
451 pr_err("Failed to find variables at %s (%d)\n", buf, ret);
452
453 free(buf);
454 return ret;
455}
456
457/* Show available variables on given probe point */
458int show_available_vars(struct perf_probe_event *pevs, int npevs,
459 int max_vls, const char *module, bool externs)
460{
461 int i, fd, ret = 0;
462
463 ret = init_vmlinux();
464 if (ret < 0)
465 return ret;
466
467 fd = open_vmlinux(module);
468 if (fd < 0) {
469 pr_warning("Failed to open debuginfo file.\n");
470 return fd;
471 }
472
473 setup_pager();
474
475 for (i = 0; i < npevs && ret >= 0; i++)
476 ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
477
478 close(fd);
479 return ret;
480}
481
381#else /* !DWARF_SUPPORT */ 482#else /* !DWARF_SUPPORT */
382 483
383static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 484static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
384 struct perf_probe_point *pp) 485 struct perf_probe_point *pp)
385{ 486{
487 struct symbol *sym;
488
489 sym = __find_kernel_function_by_name(tp->symbol, NULL);
490 if (!sym) {
491 pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
492 return -ENOENT;
493 }
386 pp->function = strdup(tp->symbol); 494 pp->function = strdup(tp->symbol);
387 if (pp->function == NULL) 495 if (pp->function == NULL)
388 return -ENOMEM; 496 return -ENOMEM;
@@ -394,7 +502,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
394 502
395static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 503static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
396 struct probe_trace_event **tevs __unused, 504 struct probe_trace_event **tevs __unused,
397 int max_tevs __unused) 505 int max_tevs __unused, const char *mod __unused)
398{ 506{
399 if (perf_probe_event_need_dwarf(pev)) { 507 if (perf_probe_event_need_dwarf(pev)) {
400 pr_warning("Debuginfo-analysis is not supported.\n"); 508 pr_warning("Debuginfo-analysis is not supported.\n");
@@ -403,12 +511,19 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
403 return 0; 511 return 0;
404} 512}
405 513
406int show_line_range(struct line_range *lr __unused) 514int show_line_range(struct line_range *lr __unused, const char *module __unused)
407{ 515{
408 pr_warning("Debuginfo-analysis is not supported.\n"); 516 pr_warning("Debuginfo-analysis is not supported.\n");
409 return -ENOSYS; 517 return -ENOSYS;
410} 518}
411 519
520int show_available_vars(struct perf_probe_event *pevs __unused,
521 int npevs __unused, int max_vls __unused,
522 const char *module __unused, bool externs __unused)
523{
524 pr_warning("Debuginfo-analysis is not supported.\n");
525 return -ENOSYS;
526}
412#endif 527#endif
413 528
414int parse_line_range_desc(const char *arg, struct line_range *lr) 529int parse_line_range_desc(const char *arg, struct line_range *lr)
@@ -1087,7 +1202,7 @@ error:
1087} 1202}
1088 1203
1089static int convert_to_perf_probe_event(struct probe_trace_event *tev, 1204static int convert_to_perf_probe_event(struct probe_trace_event *tev,
1090 struct perf_probe_event *pev) 1205 struct perf_probe_event *pev)
1091{ 1206{
1092 char buf[64] = ""; 1207 char buf[64] = "";
1093 int i, ret; 1208 int i, ret;
@@ -1516,14 +1631,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
1516 1631
1517static int convert_to_probe_trace_events(struct perf_probe_event *pev, 1632static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1518 struct probe_trace_event **tevs, 1633 struct probe_trace_event **tevs,
1519 int max_tevs) 1634 int max_tevs, const char *module)
1520{ 1635{
1521 struct symbol *sym; 1636 struct symbol *sym;
1522 int ret = 0, i; 1637 int ret = 0, i;
1523 struct probe_trace_event *tev; 1638 struct probe_trace_event *tev;
1524 1639
1525 /* Convert perf_probe_event with debuginfo */ 1640 /* Convert perf_probe_event with debuginfo */
1526 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); 1641 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
1527 if (ret != 0) 1642 if (ret != 0)
1528 return ret; 1643 return ret;
1529 1644
@@ -1539,6 +1654,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1539 goto error; 1654 goto error;
1540 } 1655 }
1541 tev->point.offset = pev->point.offset; 1656 tev->point.offset = pev->point.offset;
1657 tev->point.retprobe = pev->point.retprobe;
1542 tev->nargs = pev->nargs; 1658 tev->nargs = pev->nargs;
1543 if (tev->nargs) { 1659 if (tev->nargs) {
1544 tev->args = zalloc(sizeof(struct probe_trace_arg) 1660 tev->args = zalloc(sizeof(struct probe_trace_arg)
@@ -1571,8 +1687,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1571 } 1687 }
1572 1688
1573 /* Currently just checking function name from symbol map */ 1689 /* Currently just checking function name from symbol map */
1574 sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], 1690 sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
1575 tev->point.symbol, NULL);
1576 if (!sym) { 1691 if (!sym) {
1577 pr_warning("Kernel symbol \'%s\' not found.\n", 1692 pr_warning("Kernel symbol \'%s\' not found.\n",
1578 tev->point.symbol); 1693 tev->point.symbol);
@@ -1595,7 +1710,7 @@ struct __event_package {
1595}; 1710};
1596 1711
1597int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, 1712int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1598 bool force_add, int max_tevs) 1713 int max_tevs, const char *module, bool force_add)
1599{ 1714{
1600 int i, j, ret; 1715 int i, j, ret;
1601 struct __event_package *pkgs; 1716 struct __event_package *pkgs;
@@ -1606,15 +1721,19 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1606 1721
1607 /* Init vmlinux path */ 1722 /* Init vmlinux path */
1608 ret = init_vmlinux(); 1723 ret = init_vmlinux();
1609 if (ret < 0) 1724 if (ret < 0) {
1725 free(pkgs);
1610 return ret; 1726 return ret;
1727 }
1611 1728
1612 /* Loop 1: convert all events */ 1729 /* Loop 1: convert all events */
1613 for (i = 0; i < npevs; i++) { 1730 for (i = 0; i < npevs; i++) {
1614 pkgs[i].pev = &pevs[i]; 1731 pkgs[i].pev = &pevs[i];
1615 /* Convert with or without debuginfo */ 1732 /* Convert with or without debuginfo */
1616 ret = convert_to_probe_trace_events(pkgs[i].pev, 1733 ret = convert_to_probe_trace_events(pkgs[i].pev,
1617 &pkgs[i].tevs, max_tevs); 1734 &pkgs[i].tevs,
1735 max_tevs,
1736 module);
1618 if (ret < 0) 1737 if (ret < 0)
1619 goto end; 1738 goto end;
1620 pkgs[i].ntevs = ret; 1739 pkgs[i].ntevs = ret;
@@ -1625,10 +1744,13 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1625 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, 1744 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
1626 pkgs[i].ntevs, force_add); 1745 pkgs[i].ntevs, force_add);
1627end: 1746end:
1628 /* Loop 3: cleanup trace events */ 1747 /* Loop 3: cleanup and free trace events */
1629 for (i = 0; i < npevs; i++) 1748 for (i = 0; i < npevs; i++) {
1630 for (j = 0; j < pkgs[i].ntevs; j++) 1749 for (j = 0; j < pkgs[i].ntevs; j++)
1631 clear_probe_trace_event(&pkgs[i].tevs[j]); 1750 clear_probe_trace_event(&pkgs[i].tevs[j]);
1751 free(pkgs[i].tevs);
1752 }
1753 free(pkgs);
1632 1754
1633 return ret; 1755 return ret;
1634} 1756}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af39243a25b..5accbedfea37 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -90,6 +90,12 @@ struct line_range {
90 struct list_head line_list; /* Visible lines */ 90 struct list_head line_list; /* Visible lines */
91}; 91};
92 92
93/* List of variables */
94struct variable_list {
95 struct probe_trace_point point; /* Actual probepoint */
96 struct strlist *vars; /* Available variables */
97};
98
93/* Command string to events */ 99/* Command string to events */
94extern int parse_perf_probe_command(const char *cmd, 100extern int parse_perf_probe_command(const char *cmd,
95 struct perf_probe_event *pev); 101 struct perf_probe_event *pev);
@@ -109,12 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
109/* Command string to line-range */ 115/* Command string to line-range */
110extern int parse_line_range_desc(const char *cmd, struct line_range *lr); 116extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
111 117
118/* Internal use: Return kernel/module path */
119extern const char *kernel_get_module_path(const char *module);
112 120
113extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, 121extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
114 bool force_add, int max_probe_points); 122 int max_probe_points, const char *module,
123 bool force_add);
115extern int del_perf_probe_events(struct strlist *dellist); 124extern int del_perf_probe_events(struct strlist *dellist);
116extern int show_perf_probe_events(void); 125extern int show_perf_probe_events(void);
117extern int show_line_range(struct line_range *lr); 126extern int show_line_range(struct line_range *lr, const char *module);
127extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
128 int max_probe_points, const char *module,
129 bool externs);
118 130
119 131
120/* Maximum index number of event-name postfix */ 132/* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 840f1aabbb74..ddf4d4556321 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -33,7 +33,6 @@
33#include <ctype.h> 33#include <ctype.h>
34#include <dwarf-regs.h> 34#include <dwarf-regs.h>
35 35
36#include "string.h"
37#include "event.h" 36#include "event.h"
38#include "debug.h" 37#include "debug.h"
39#include "util.h" 38#include "util.h"
@@ -117,6 +116,126 @@ static void line_list__free(struct list_head *head)
117 } 116 }
118} 117}
119 118
119/* Dwarf FL wrappers */
120static char *debuginfo_path; /* Currently dummy */
121
122static const Dwfl_Callbacks offline_callbacks = {
123 .find_debuginfo = dwfl_standard_find_debuginfo,
124 .debuginfo_path = &debuginfo_path,
125
126 .section_address = dwfl_offline_section_address,
127
128 /* We use this table for core files too. */
129 .find_elf = dwfl_build_id_find_elf,
130};
131
132/* Get a Dwarf from offline image */
133static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
134{
135 Dwfl_Module *mod;
136 Dwarf *dbg = NULL;
137
138 if (!dwflp)
139 return NULL;
140
141 *dwflp = dwfl_begin(&offline_callbacks);
142 if (!*dwflp)
143 return NULL;
144
145 mod = dwfl_report_offline(*dwflp, "", "", fd);
146 if (!mod)
147 goto error;
148
149 dbg = dwfl_module_getdwarf(mod, bias);
150 if (!dbg) {
151error:
152 dwfl_end(*dwflp);
153 *dwflp = NULL;
154 }
155 return dbg;
156}
157
158#if _ELFUTILS_PREREQ(0, 148)
159/* This method is buggy if elfutils is older than 0.148 */
160static int __linux_kernel_find_elf(Dwfl_Module *mod,
161 void **userdata,
162 const char *module_name,
163 Dwarf_Addr base,
164 char **file_name, Elf **elfp)
165{
166 int fd;
167 const char *path = kernel_get_module_path(module_name);
168
169 pr_debug2("Use file %s for %s\n", path, module_name);
170 if (path) {
171 fd = open(path, O_RDONLY);
172 if (fd >= 0) {
173 *file_name = strdup(path);
174 return fd;
175 }
176 }
177 /* If failed, try to call standard method */
178 return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
179 file_name, elfp);
180}
181
182static const Dwfl_Callbacks kernel_callbacks = {
183 .find_debuginfo = dwfl_standard_find_debuginfo,
184 .debuginfo_path = &debuginfo_path,
185
186 .find_elf = __linux_kernel_find_elf,
187 .section_address = dwfl_linux_kernel_module_section_address,
188};
189
190/* Get a Dwarf from live kernel image */
191static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
192 Dwarf_Addr *bias)
193{
194 Dwarf *dbg;
195
196 if (!dwflp)
197 return NULL;
198
199 *dwflp = dwfl_begin(&kernel_callbacks);
200 if (!*dwflp)
201 return NULL;
202
203 /* Load the kernel dwarves: Don't care the result here */
204 dwfl_linux_kernel_report_kernel(*dwflp);
205 dwfl_linux_kernel_report_modules(*dwflp);
206
207 dbg = dwfl_addrdwarf(*dwflp, addr, bias);
208 /* Here, check whether we could get a real dwarf */
209 if (!dbg) {
210 pr_debug("Failed to find kernel dwarf at %lx\n",
211 (unsigned long)addr);
212 dwfl_end(*dwflp);
213 *dwflp = NULL;
214 }
215 return dbg;
216}
217#else
218/* With older elfutils, this just support kernel module... */
219static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp,
220 Dwarf_Addr *bias)
221{
222 int fd;
223 const char *path = kernel_get_module_path("kernel");
224
225 if (!path) {
226 pr_err("Failed to find vmlinux path\n");
227 return NULL;
228 }
229
230 pr_debug2("Use file %s for debuginfo\n", path);
231 fd = open(path, O_RDONLY);
232 if (fd < 0)
233 return NULL;
234
235 return dwfl_init_offline_dwarf(fd, dwflp, bias);
236}
237#endif
238
120/* Dwarf wrappers */ 239/* Dwarf wrappers */
121 240
122/* Find the realpath of the target file. */ 241/* Find the realpath of the target file. */
@@ -161,26 +280,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
161 return name ? (strcmp(tname, name) == 0) : false; 280 return name ? (strcmp(tname, name) == 0) : false;
162} 281}
163 282
164/* Get type die, but skip qualifiers and typedef */ 283/* Get type die */
165static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 284static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
166{ 285{
167 Dwarf_Attribute attr; 286 Dwarf_Attribute attr;
287
288 if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
289 dwarf_formref_die(&attr, die_mem))
290 return die_mem;
291 else
292 return NULL;
293}
294
295/* Get a type die, but skip qualifiers */
296static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
297{
168 int tag; 298 int tag;
169 299
170 do { 300 do {
171 if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || 301 vr_die = die_get_type(vr_die, die_mem);
172 dwarf_formref_die(&attr, die_mem) == NULL) 302 if (!vr_die)
173 return NULL; 303 break;
174 304 tag = dwarf_tag(vr_die);
175 tag = dwarf_tag(die_mem);
176 vr_die = die_mem;
177 } while (tag == DW_TAG_const_type || 305 } while (tag == DW_TAG_const_type ||
178 tag == DW_TAG_restrict_type || 306 tag == DW_TAG_restrict_type ||
179 tag == DW_TAG_volatile_type || 307 tag == DW_TAG_volatile_type ||
180 tag == DW_TAG_shared_type || 308 tag == DW_TAG_shared_type);
181 tag == DW_TAG_typedef);
182 309
183 return die_mem; 310 return vr_die;
311}
312
313/* Get a type die, but skip qualifiers and typedef */
314static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
315{
316 do {
317 vr_die = __die_get_real_type(vr_die, die_mem);
318 } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
319
320 return vr_die;
184} 321}
185 322
186static bool die_is_signed_type(Dwarf_Die *tp_die) 323static bool die_is_signed_type(Dwarf_Die *tp_die)
@@ -321,25 +458,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
321 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); 458 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
322} 459}
323 460
461struct __find_variable_param {
462 const char *name;
463 Dwarf_Addr addr;
464};
465
324static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) 466static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
325{ 467{
326 const char *name = data; 468 struct __find_variable_param *fvp = data;
327 int tag; 469 int tag;
328 470
329 tag = dwarf_tag(die_mem); 471 tag = dwarf_tag(die_mem);
330 if ((tag == DW_TAG_formal_parameter || 472 if ((tag == DW_TAG_formal_parameter ||
331 tag == DW_TAG_variable) && 473 tag == DW_TAG_variable) &&
332 die_compare_name(die_mem, name)) 474 die_compare_name(die_mem, fvp->name))
333 return DIE_FIND_CB_FOUND; 475 return DIE_FIND_CB_FOUND;
334 476
335 return DIE_FIND_CB_CONTINUE; 477 if (dwarf_haspc(die_mem, fvp->addr))
478 return DIE_FIND_CB_CONTINUE;
479 else
480 return DIE_FIND_CB_SIBLING;
336} 481}
337 482
338/* Find a variable called 'name' */ 483/* Find a variable called 'name' at given address */
339static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, 484static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
340 Dwarf_Die *die_mem) 485 Dwarf_Addr addr, Dwarf_Die *die_mem)
341{ 486{
342 return die_find_child(sp_die, __die_find_variable_cb, (void *)name, 487 struct __find_variable_param fvp = { .name = name, .addr = addr};
488
489 return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
343 die_mem); 490 die_mem);
344} 491}
345 492
@@ -362,6 +509,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
362 die_mem); 509 die_mem);
363} 510}
364 511
512/* Get the name of given variable DIE */
513static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
514{
515 Dwarf_Die type;
516 int tag, ret, ret2;
517 const char *tmp = "";
518
519 if (__die_get_real_type(vr_die, &type) == NULL)
520 return -ENOENT;
521
522 tag = dwarf_tag(&type);
523 if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
524 tmp = "*";
525 else if (tag == DW_TAG_subroutine_type) {
526 /* Function pointer */
527 ret = snprintf(buf, len, "(function_type)");
528 return (ret >= len) ? -E2BIG : ret;
529 } else {
530 if (!dwarf_diename(&type))
531 return -ENOENT;
532 if (tag == DW_TAG_union_type)
533 tmp = "union ";
534 else if (tag == DW_TAG_structure_type)
535 tmp = "struct ";
536 /* Write a base name */
537 ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
538 return (ret >= len) ? -E2BIG : ret;
539 }
540 ret = die_get_typename(&type, buf, len);
541 if (ret > 0) {
542 ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
543 ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
544 }
545 return ret;
546}
547
548/* Get the name and type of given variable DIE, stored as "type\tname" */
549static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
550{
551 int ret, ret2;
552
553 ret = die_get_typename(vr_die, buf, len);
554 if (ret < 0) {
555 pr_debug("Failed to get type, make it unknown.\n");
556 ret = snprintf(buf, len, "(unknown_type)");
557 }
558 if (ret > 0) {
559 ret2 = snprintf(buf + ret, len - ret, "\t%s",
560 dwarf_diename(vr_die));
561 ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
562 }
563 return ret;
564}
565
365/* 566/*
366 * Probe finder related functions 567 * Probe finder related functions
367 */ 568 */
@@ -375,8 +576,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
375 return ref; 576 return ref;
376} 577}
377 578
378/* Show a location */ 579/*
379static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) 580 * Convert a location into trace_arg.
581 * If tvar == NULL, this just checks variable can be converted.
582 */
583static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
584 Dwarf_Op *fb_ops,
585 struct probe_trace_arg *tvar)
380{ 586{
381 Dwarf_Attribute attr; 587 Dwarf_Attribute attr;
382 Dwarf_Op *op; 588 Dwarf_Op *op;
@@ -385,20 +591,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
385 Dwarf_Word offs = 0; 591 Dwarf_Word offs = 0;
386 bool ref = false; 592 bool ref = false;
387 const char *regs; 593 const char *regs;
388 struct probe_trace_arg *tvar = pf->tvar;
389 int ret; 594 int ret;
390 595
596 if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
597 goto static_var;
598
391 /* TODO: handle more than 1 exprs */ 599 /* TODO: handle more than 1 exprs */
392 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || 600 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
393 dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || 601 dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
394 nops == 0) { 602 nops == 0) {
395 /* TODO: Support const_value */ 603 /* TODO: Support const_value */
396 pr_err("Failed to find the location of %s at this address.\n"
397 " Perhaps, it has been optimized out.\n", pf->pvar->var);
398 return -ENOENT; 604 return -ENOENT;
399 } 605 }
400 606
401 if (op->atom == DW_OP_addr) { 607 if (op->atom == DW_OP_addr) {
608static_var:
609 if (!tvar)
610 return 0;
402 /* Static variables on memory (not stack), make @varname */ 611 /* Static variables on memory (not stack), make @varname */
403 ret = strlen(dwarf_diename(vr_die)); 612 ret = strlen(dwarf_diename(vr_die));
404 tvar->value = zalloc(ret + 2); 613 tvar->value = zalloc(ret + 2);
@@ -413,14 +622,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
413 622
414 /* If this is based on frame buffer, set the offset */ 623 /* If this is based on frame buffer, set the offset */
415 if (op->atom == DW_OP_fbreg) { 624 if (op->atom == DW_OP_fbreg) {
416 if (pf->fb_ops == NULL) { 625 if (fb_ops == NULL)
417 pr_warning("The attribute of frame base is not "
418 "supported.\n");
419 return -ENOTSUP; 626 return -ENOTSUP;
420 }
421 ref = true; 627 ref = true;
422 offs = op->number; 628 offs = op->number;
423 op = &pf->fb_ops[0]; 629 op = &fb_ops[0];
424 } 630 }
425 631
426 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { 632 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
@@ -436,13 +642,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
436 } else if (op->atom == DW_OP_regx) { 642 } else if (op->atom == DW_OP_regx) {
437 regn = op->number; 643 regn = op->number;
438 } else { 644 } else {
439 pr_warning("DW_OP %x is not supported.\n", op->atom); 645 pr_debug("DW_OP %x is not supported.\n", op->atom);
440 return -ENOTSUP; 646 return -ENOTSUP;
441 } 647 }
442 648
649 if (!tvar)
650 return 0;
651
443 regs = get_arch_regstr(regn); 652 regs = get_arch_regstr(regn);
444 if (!regs) { 653 if (!regs) {
445 pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); 654 /* This should be a bug in DWARF or this tool */
655 pr_warning("Mapping for DWARF register number %u "
656 "missing on this architecture.", regn);
446 return -ERANGE; 657 return -ERANGE;
447 } 658 }
448 659
@@ -667,8 +878,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
667 pr_debug("Converting variable %s into trace event.\n", 878 pr_debug("Converting variable %s into trace event.\n",
668 dwarf_diename(vr_die)); 879 dwarf_diename(vr_die));
669 880
670 ret = convert_variable_location(vr_die, pf); 881 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
671 if (ret == 0 && pf->pvar->field) { 882 pf->tvar);
883 if (ret == -ENOENT)
884 pr_err("Failed to find the location of %s at this address.\n"
885 " Perhaps, it has been optimized out.\n", pf->pvar->var);
886 else if (ret == -ENOTSUP)
887 pr_err("Sorry, we don't support this variable location yet.\n");
888 else if (pf->pvar->field) {
672 ret = convert_variable_fields(vr_die, pf->pvar->var, 889 ret = convert_variable_fields(vr_die, pf->pvar->var,
673 pf->pvar->field, &pf->tvar->ref, 890 pf->pvar->field, &pf->tvar->ref,
674 &die_mem); 891 &die_mem);
@@ -687,6 +904,25 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
687 char buf[32], *ptr; 904 char buf[32], *ptr;
688 int ret, nscopes; 905 int ret, nscopes;
689 906
907 if (!is_c_varname(pf->pvar->var)) {
908 /* Copy raw parameters */
909 pf->tvar->value = strdup(pf->pvar->var);
910 if (pf->tvar->value == NULL)
911 return -ENOMEM;
912 if (pf->pvar->type) {
913 pf->tvar->type = strdup(pf->pvar->type);
914 if (pf->tvar->type == NULL)
915 return -ENOMEM;
916 }
917 if (pf->pvar->name) {
918 pf->tvar->name = strdup(pf->pvar->name);
919 if (pf->tvar->name == NULL)
920 return -ENOMEM;
921 } else
922 pf->tvar->name = NULL;
923 return 0;
924 }
925
690 if (pf->pvar->name) 926 if (pf->pvar->name)
691 pf->tvar->name = strdup(pf->pvar->name); 927 pf->tvar->name = strdup(pf->pvar->name);
692 else { 928 else {
@@ -701,68 +937,42 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
701 if (pf->tvar->name == NULL) 937 if (pf->tvar->name == NULL)
702 return -ENOMEM; 938 return -ENOMEM;
703 939
704 if (!is_c_varname(pf->pvar->var)) {
705 /* Copy raw parameters */
706 pf->tvar->value = strdup(pf->pvar->var);
707 if (pf->tvar->value == NULL)
708 return -ENOMEM;
709 else
710 return 0;
711 }
712
713 pr_debug("Searching '%s' variable in context.\n", 940 pr_debug("Searching '%s' variable in context.\n",
714 pf->pvar->var); 941 pf->pvar->var);
715 /* Search child die for local variables and parameters. */ 942 /* Search child die for local variables and parameters. */
716 if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) 943 if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
717 ret = convert_variable(&vr_die, pf); 944 ret = convert_variable(&vr_die, pf);
718 else { 945 else {
719 /* Search upper class */ 946 /* Search upper class */
720 nscopes = dwarf_getscopes_die(sp_die, &scopes); 947 nscopes = dwarf_getscopes_die(sp_die, &scopes);
721 if (nscopes > 0) { 948 while (nscopes-- > 1) {
722 ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, 949 pr_debug("Searching variables in %s\n",
723 0, NULL, 0, 0, &vr_die); 950 dwarf_diename(&scopes[nscopes]));
724 if (ret >= 0) 951 /* We should check this scope, so give dummy address */
952 if (die_find_variable_at(&scopes[nscopes],
953 pf->pvar->var, 0,
954 &vr_die)) {
725 ret = convert_variable(&vr_die, pf); 955 ret = convert_variable(&vr_die, pf);
726 else 956 goto found;
727 ret = -ENOENT; 957 }
958 }
959 if (scopes)
728 free(scopes); 960 free(scopes);
729 } else 961 ret = -ENOENT;
730 ret = -ENOENT;
731 } 962 }
963found:
732 if (ret < 0) 964 if (ret < 0)
733 pr_warning("Failed to find '%s' in this function.\n", 965 pr_warning("Failed to find '%s' in this function.\n",
734 pf->pvar->var); 966 pf->pvar->var);
735 return ret; 967 return ret;
736} 968}
737 969
738/* Show a probe point to output buffer */ 970/* Convert subprogram DIE to trace point */
739static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) 971static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
972 bool retprobe, struct probe_trace_point *tp)
740{ 973{
741 struct probe_trace_event *tev;
742 Dwarf_Addr eaddr; 974 Dwarf_Addr eaddr;
743 Dwarf_Die die_mem;
744 const char *name; 975 const char *name;
745 int ret, i;
746 Dwarf_Attribute fb_attr;
747 size_t nops;
748
749 if (pf->ntevs == pf->max_tevs) {
750 pr_warning("Too many( > %d) probe point found.\n",
751 pf->max_tevs);
752 return -ERANGE;
753 }
754 tev = &pf->tevs[pf->ntevs++];
755
756 /* If no real subprogram, find a real one */
757 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
758 sp_die = die_find_real_subprogram(&pf->cu_die,
759 pf->addr, &die_mem);
760 if (!sp_die) {
761 pr_warning("Failed to find probe point in any "
762 "functions.\n");
763 return -ENOENT;
764 }
765 }
766 976
767 /* Copy the name of probe point */ 977 /* Copy the name of probe point */
768 name = dwarf_diename(sp_die); 978 name = dwarf_diename(sp_die);
@@ -772,16 +982,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
772 dwarf_diename(sp_die)); 982 dwarf_diename(sp_die));
773 return -ENOENT; 983 return -ENOENT;
774 } 984 }
775 tev->point.symbol = strdup(name); 985 tp->symbol = strdup(name);
776 if (tev->point.symbol == NULL) 986 if (tp->symbol == NULL)
777 return -ENOMEM; 987 return -ENOMEM;
778 tev->point.offset = (unsigned long)(pf->addr - eaddr); 988 tp->offset = (unsigned long)(paddr - eaddr);
779 } else 989 } else
780 /* This function has no name. */ 990 /* This function has no name. */
781 tev->point.offset = (unsigned long)pf->addr; 991 tp->offset = (unsigned long)paddr;
782 992
783 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, 993 /* Return probe must be on the head of a subprogram */
784 tev->point.offset); 994 if (retprobe) {
995 if (eaddr != paddr) {
996 pr_warning("Return probe must be on the head of"
997 " a real function\n");
998 return -EINVAL;
999 }
1000 tp->retprobe = true;
1001 }
1002
1003 return 0;
1004}
1005
1006/* Call probe_finder callback with real subprogram DIE */
1007static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
1008{
1009 Dwarf_Die die_mem;
1010 Dwarf_Attribute fb_attr;
1011 size_t nops;
1012 int ret;
1013
1014 /* If no real subprogram, find a real one */
1015 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
1016 sp_die = die_find_real_subprogram(&pf->cu_die,
1017 pf->addr, &die_mem);
1018 if (!sp_die) {
1019 pr_warning("Failed to find probe point in any "
1020 "functions.\n");
1021 return -ENOENT;
1022 }
1023 }
785 1024
786 /* Get the frame base attribute/ops */ 1025 /* Get the frame base attribute/ops */
787 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); 1026 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
@@ -801,22 +1040,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
801#endif 1040#endif
802 } 1041 }
803 1042
804 /* Find each argument */ 1043 /* Call finder's callback handler */
805 tev->nargs = pf->pev->nargs; 1044 ret = pf->callback(sp_die, pf);
806 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
807 if (tev->args == NULL)
808 return -ENOMEM;
809 for (i = 0; i < pf->pev->nargs; i++) {
810 pf->pvar = &pf->pev->args[i];
811 pf->tvar = &tev->args[i];
812 ret = find_variable(sp_die, pf);
813 if (ret != 0)
814 return ret;
815 }
816 1045
817 /* *pf->fb_ops will be cached in libdw. Don't free it. */ 1046 /* *pf->fb_ops will be cached in libdw. Don't free it. */
818 pf->fb_ops = NULL; 1047 pf->fb_ops = NULL;
819 return 0; 1048
1049 return ret;
820} 1050}
821 1051
822/* Find probe point from its line number */ 1052/* Find probe point from its line number */
@@ -852,7 +1082,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
852 (int)i, lineno, (uintmax_t)addr); 1082 (int)i, lineno, (uintmax_t)addr);
853 pf->addr = addr; 1083 pf->addr = addr;
854 1084
855 ret = convert_probe_point(NULL, pf); 1085 ret = call_probe_finder(NULL, pf);
856 /* Continuing, because target line might be inlined. */ 1086 /* Continuing, because target line might be inlined. */
857 } 1087 }
858 return ret; 1088 return ret;
@@ -965,7 +1195,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
965 (int)i, lineno, (unsigned long long)addr); 1195 (int)i, lineno, (unsigned long long)addr);
966 pf->addr = addr; 1196 pf->addr = addr;
967 1197
968 ret = convert_probe_point(sp_die, pf); 1198 ret = call_probe_finder(sp_die, pf);
969 /* Continuing, because target line might be inlined. */ 1199 /* Continuing, because target line might be inlined. */
970 } 1200 }
971 /* TODO: deallocate lines, but how? */ 1201 /* TODO: deallocate lines, but how? */
@@ -1000,7 +1230,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
1000 pr_debug("found inline addr: 0x%jx\n", 1230 pr_debug("found inline addr: 0x%jx\n",
1001 (uintmax_t)pf->addr); 1231 (uintmax_t)pf->addr);
1002 1232
1003 param->retval = convert_probe_point(in_die, pf); 1233 param->retval = call_probe_finder(in_die, pf);
1004 if (param->retval < 0) 1234 if (param->retval < 0)
1005 return DWARF_CB_ABORT; 1235 return DWARF_CB_ABORT;
1006 } 1236 }
@@ -1038,7 +1268,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
1038 } 1268 }
1039 pf->addr += pp->offset; 1269 pf->addr += pp->offset;
1040 /* TODO: Check the address in this function */ 1270 /* TODO: Check the address in this function */
1041 param->retval = convert_probe_point(sp_die, pf); 1271 param->retval = call_probe_finder(sp_die, pf);
1042 } 1272 }
1043 } else { 1273 } else {
1044 struct dwarf_callback_param _param = {.data = (void *)pf, 1274 struct dwarf_callback_param _param = {.data = (void *)pf,
@@ -1060,90 +1290,276 @@ static int find_probe_point_by_func(struct probe_finder *pf)
1060 return _param.retval; 1290 return _param.retval;
1061} 1291}
1062 1292
1063/* Find probe_trace_events specified by perf_probe_event from debuginfo */ 1293/* Find probe points from debuginfo */
1064int find_probe_trace_events(int fd, struct perf_probe_event *pev, 1294static int find_probes(int fd, struct probe_finder *pf)
1065 struct probe_trace_event **tevs, int max_tevs)
1066{ 1295{
1067 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; 1296 struct perf_probe_point *pp = &pf->pev->point;
1068 struct perf_probe_point *pp = &pev->point;
1069 Dwarf_Off off, noff; 1297 Dwarf_Off off, noff;
1070 size_t cuhl; 1298 size_t cuhl;
1071 Dwarf_Die *diep; 1299 Dwarf_Die *diep;
1072 Dwarf *dbg; 1300 Dwarf *dbg = NULL;
1301 Dwfl *dwfl;
1302 Dwarf_Addr bias; /* Currently ignored */
1073 int ret = 0; 1303 int ret = 0;
1074 1304
1075 pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); 1305 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1076 if (pf.tevs == NULL)
1077 return -ENOMEM;
1078 *tevs = pf.tevs;
1079 pf.ntevs = 0;
1080
1081 dbg = dwarf_begin(fd, DWARF_C_READ);
1082 if (!dbg) { 1306 if (!dbg) {
1083 pr_warning("No dwarf info found in the vmlinux - " 1307 pr_warning("No dwarf info found in the vmlinux - "
1084 "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1308 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1085 free(pf.tevs);
1086 *tevs = NULL;
1087 return -EBADF; 1309 return -EBADF;
1088 } 1310 }
1089 1311
1090#if _ELFUTILS_PREREQ(0, 142) 1312#if _ELFUTILS_PREREQ(0, 142)
1091 /* Get the call frame information from this dwarf */ 1313 /* Get the call frame information from this dwarf */
1092 pf.cfi = dwarf_getcfi(dbg); 1314 pf->cfi = dwarf_getcfi(dbg);
1093#endif 1315#endif
1094 1316
1095 off = 0; 1317 off = 0;
1096 line_list__init(&pf.lcache); 1318 line_list__init(&pf->lcache);
1097 /* Loop on CUs (Compilation Unit) */ 1319 /* Loop on CUs (Compilation Unit) */
1098 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && 1320 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
1099 ret >= 0) { 1321 ret >= 0) {
1100 /* Get the DIE(Debugging Information Entry) of this CU */ 1322 /* Get the DIE(Debugging Information Entry) of this CU */
1101 diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); 1323 diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
1102 if (!diep) 1324 if (!diep)
1103 continue; 1325 continue;
1104 1326
1105 /* Check if target file is included. */ 1327 /* Check if target file is included. */
1106 if (pp->file) 1328 if (pp->file)
1107 pf.fname = cu_find_realpath(&pf.cu_die, pp->file); 1329 pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
1108 else 1330 else
1109 pf.fname = NULL; 1331 pf->fname = NULL;
1110 1332
1111 if (!pp->file || pf.fname) { 1333 if (!pp->file || pf->fname) {
1112 if (pp->function) 1334 if (pp->function)
1113 ret = find_probe_point_by_func(&pf); 1335 ret = find_probe_point_by_func(pf);
1114 else if (pp->lazy_line) 1336 else if (pp->lazy_line)
1115 ret = find_probe_point_lazy(NULL, &pf); 1337 ret = find_probe_point_lazy(NULL, pf);
1116 else { 1338 else {
1117 pf.lno = pp->line; 1339 pf->lno = pp->line;
1118 ret = find_probe_point_by_line(&pf); 1340 ret = find_probe_point_by_line(pf);
1119 } 1341 }
1120 } 1342 }
1121 off = noff; 1343 off = noff;
1122 } 1344 }
1123 line_list__free(&pf.lcache); 1345 line_list__free(&pf->lcache);
1124 dwarf_end(dbg); 1346 if (dwfl)
1347 dwfl_end(dwfl);
1348
1349 return ret;
1350}
1351
1352/* Add a found probe point into trace event list */
1353static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
1354{
1355 struct trace_event_finder *tf =
1356 container_of(pf, struct trace_event_finder, pf);
1357 struct probe_trace_event *tev;
1358 int ret, i;
1359
1360 /* Check number of tevs */
1361 if (tf->ntevs == tf->max_tevs) {
1362 pr_warning("Too many( > %d) probe point found.\n",
1363 tf->max_tevs);
1364 return -ERANGE;
1365 }
1366 tev = &tf->tevs[tf->ntevs++];
1367
1368 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1369 &tev->point);
1370 if (ret < 0)
1371 return ret;
1372
1373 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
1374 tev->point.offset);
1375
1376 /* Find each argument */
1377 tev->nargs = pf->pev->nargs;
1378 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
1379 if (tev->args == NULL)
1380 return -ENOMEM;
1381 for (i = 0; i < pf->pev->nargs; i++) {
1382 pf->pvar = &pf->pev->args[i];
1383 pf->tvar = &tev->args[i];
1384 ret = find_variable(sp_die, pf);
1385 if (ret != 0)
1386 return ret;
1387 }
1125 1388
1126 return (ret < 0) ? ret : pf.ntevs; 1389 return 0;
1390}
1391
1392/* Find probe_trace_events specified by perf_probe_event from debuginfo */
1393int find_probe_trace_events(int fd, struct perf_probe_event *pev,
1394 struct probe_trace_event **tevs, int max_tevs)
1395{
1396 struct trace_event_finder tf = {
1397 .pf = {.pev = pev, .callback = add_probe_trace_event},
1398 .max_tevs = max_tevs};
1399 int ret;
1400
1401 /* Allocate result tevs array */
1402 *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
1403 if (*tevs == NULL)
1404 return -ENOMEM;
1405
1406 tf.tevs = *tevs;
1407 tf.ntevs = 0;
1408
1409 ret = find_probes(fd, &tf.pf);
1410 if (ret < 0) {
1411 free(*tevs);
1412 *tevs = NULL;
1413 return ret;
1414 }
1415
1416 return (ret < 0) ? ret : tf.ntevs;
1417}
1418
1419#define MAX_VAR_LEN 64
1420
1421/* Collect available variables in this scope */
1422static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
1423{
1424 struct available_var_finder *af = data;
1425 struct variable_list *vl;
1426 char buf[MAX_VAR_LEN];
1427 int tag, ret;
1428
1429 vl = &af->vls[af->nvls - 1];
1430
1431 tag = dwarf_tag(die_mem);
1432 if (tag == DW_TAG_formal_parameter ||
1433 tag == DW_TAG_variable) {
1434 ret = convert_variable_location(die_mem, af->pf.addr,
1435 af->pf.fb_ops, NULL);
1436 if (ret == 0) {
1437 ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
1438 pr_debug2("Add new var: %s\n", buf);
1439 if (ret > 0)
1440 strlist__add(vl->vars, buf);
1441 }
1442 }
1443
1444 if (af->child && dwarf_haspc(die_mem, af->pf.addr))
1445 return DIE_FIND_CB_CONTINUE;
1446 else
1447 return DIE_FIND_CB_SIBLING;
1448}
1449
1450/* Add a found vars into available variables list */
1451static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
1452{
1453 struct available_var_finder *af =
1454 container_of(pf, struct available_var_finder, pf);
1455 struct variable_list *vl;
1456 Dwarf_Die die_mem, *scopes = NULL;
1457 int ret, nscopes;
1458
1459 /* Check number of tevs */
1460 if (af->nvls == af->max_vls) {
1461 pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
1462 return -ERANGE;
1463 }
1464 vl = &af->vls[af->nvls++];
1465
1466 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1467 &vl->point);
1468 if (ret < 0)
1469 return ret;
1470
1471 pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
1472 vl->point.offset);
1473
1474 /* Find local variables */
1475 vl->vars = strlist__new(true, NULL);
1476 if (vl->vars == NULL)
1477 return -ENOMEM;
1478 af->child = true;
1479 die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
1480
1481 /* Find external variables */
1482 if (!af->externs)
1483 goto out;
1484 /* Don't need to search child DIE for externs. */
1485 af->child = false;
1486 nscopes = dwarf_getscopes_die(sp_die, &scopes);
1487 while (nscopes-- > 1)
1488 die_find_child(&scopes[nscopes], collect_variables_cb,
1489 (void *)af, &die_mem);
1490 if (scopes)
1491 free(scopes);
1492
1493out:
1494 if (strlist__empty(vl->vars)) {
1495 strlist__delete(vl->vars);
1496 vl->vars = NULL;
1497 }
1498
1499 return ret;
1500}
1501
1502/* Find available variables at given probe point */
1503int find_available_vars_at(int fd, struct perf_probe_event *pev,
1504 struct variable_list **vls, int max_vls,
1505 bool externs)
1506{
1507 struct available_var_finder af = {
1508 .pf = {.pev = pev, .callback = add_available_vars},
1509 .max_vls = max_vls, .externs = externs};
1510 int ret;
1511
1512 /* Allocate result vls array */
1513 *vls = zalloc(sizeof(struct variable_list) * max_vls);
1514 if (*vls == NULL)
1515 return -ENOMEM;
1516
1517 af.vls = *vls;
1518 af.nvls = 0;
1519
1520 ret = find_probes(fd, &af.pf);
1521 if (ret < 0) {
1522 /* Free vlist for error */
1523 while (af.nvls--) {
1524 if (af.vls[af.nvls].point.symbol)
1525 free(af.vls[af.nvls].point.symbol);
1526 if (af.vls[af.nvls].vars)
1527 strlist__delete(af.vls[af.nvls].vars);
1528 }
1529 free(af.vls);
1530 *vls = NULL;
1531 return ret;
1532 }
1533
1534 return (ret < 0) ? ret : af.nvls;
1127} 1535}
1128 1536
1129/* Reverse search */ 1537/* Reverse search */
1130int find_perf_probe_point(int fd, unsigned long addr, 1538int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
1131 struct perf_probe_point *ppt)
1132{ 1539{
1133 Dwarf_Die cudie, spdie, indie; 1540 Dwarf_Die cudie, spdie, indie;
1134 Dwarf *dbg; 1541 Dwarf *dbg = NULL;
1542 Dwfl *dwfl = NULL;
1135 Dwarf_Line *line; 1543 Dwarf_Line *line;
1136 Dwarf_Addr laddr, eaddr; 1544 Dwarf_Addr laddr, eaddr, bias = 0;
1137 const char *tmp; 1545 const char *tmp;
1138 int lineno, ret = 0; 1546 int lineno, ret = 0;
1139 bool found = false; 1547 bool found = false;
1140 1548
1141 dbg = dwarf_begin(fd, DWARF_C_READ); 1549 /* Open the live linux kernel */
1142 if (!dbg) 1550 dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
1143 return -EBADF; 1551 if (!dbg) {
1552 pr_warning("No dwarf info found in the vmlinux - "
1553 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1554 ret = -EINVAL;
1555 goto end;
1556 }
1144 1557
1558 /* Adjust address with bias */
1559 addr += bias;
1145 /* Find cu die */ 1560 /* Find cu die */
1146 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { 1561 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
1562 pr_warning("No CU DIE is found at %lx\n", addr);
1147 ret = -EINVAL; 1563 ret = -EINVAL;
1148 goto end; 1564 goto end;
1149 } 1565 }
@@ -1206,7 +1622,8 @@ found:
1206 } 1622 }
1207 1623
1208end: 1624end:
1209 dwarf_end(dbg); 1625 if (dwfl)
1626 dwfl_end(dwfl);
1210 if (ret >= 0) 1627 if (ret >= 0)
1211 ret = found ? 1 : 0; 1628 ret = found ? 1 : 0;
1212 return ret; 1629 return ret;
@@ -1339,6 +1756,9 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1339 struct line_finder *lf = param->data; 1756 struct line_finder *lf = param->data;
1340 struct line_range *lr = lf->lr; 1757 struct line_range *lr = lf->lr;
1341 1758
1759 pr_debug("find (%llx) %s\n",
1760 (unsigned long long)dwarf_dieoffset(sp_die),
1761 dwarf_diename(sp_die));
1342 if (dwarf_tag(sp_die) == DW_TAG_subprogram && 1762 if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
1343 die_compare_name(sp_die, lr->function)) { 1763 die_compare_name(sp_die, lr->function)) {
1344 lf->fname = dwarf_decl_file(sp_die); 1764 lf->fname = dwarf_decl_file(sp_die);
@@ -1382,10 +1802,12 @@ int find_line_range(int fd, struct line_range *lr)
1382 Dwarf_Off off = 0, noff; 1802 Dwarf_Off off = 0, noff;
1383 size_t cuhl; 1803 size_t cuhl;
1384 Dwarf_Die *diep; 1804 Dwarf_Die *diep;
1385 Dwarf *dbg; 1805 Dwarf *dbg = NULL;
1806 Dwfl *dwfl;
1807 Dwarf_Addr bias; /* Currently ignored */
1386 const char *comp_dir; 1808 const char *comp_dir;
1387 1809
1388 dbg = dwarf_begin(fd, DWARF_C_READ); 1810 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1389 if (!dbg) { 1811 if (!dbg) {
1390 pr_warning("No dwarf info found in the vmlinux - " 1812 pr_warning("No dwarf info found in the vmlinux - "
1391 "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1813 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1431,8 +1853,7 @@ int find_line_range(int fd, struct line_range *lr)
1431 } 1853 }
1432 1854
1433 pr_debug("path: %s\n", lr->path); 1855 pr_debug("path: %s\n", lr->path);
1434 dwarf_end(dbg); 1856 dwfl_end(dwfl);
1435
1436 return (ret < 0) ? ret : lf.found; 1857 return (ret < 0) ? ret : lf.found;
1437} 1858}
1438 1859
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 4507d519f183..bba69d455699 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -22,20 +22,27 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
22 int max_tevs); 22 int max_tevs);
23 23
24/* Find a perf_probe_point from debuginfo */ 24/* Find a perf_probe_point from debuginfo */
25extern int find_perf_probe_point(int fd, unsigned long addr, 25extern int find_perf_probe_point(unsigned long addr,
26 struct perf_probe_point *ppt); 26 struct perf_probe_point *ppt);
27 27
28/* Find a line range */
28extern int find_line_range(int fd, struct line_range *lr); 29extern int find_line_range(int fd, struct line_range *lr);
29 30
31/* Find available variables */
32extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
33 struct variable_list **vls, int max_points,
34 bool externs);
35
30#include <dwarf.h> 36#include <dwarf.h>
31#include <libdw.h> 37#include <libdw.h>
38#include <libdwfl.h>
32#include <version.h> 39#include <version.h>
33 40
34struct probe_finder { 41struct probe_finder {
35 struct perf_probe_event *pev; /* Target probe event */ 42 struct perf_probe_event *pev; /* Target probe event */
36 struct probe_trace_event *tevs; /* Result trace events */ 43
37 int ntevs; /* Number of trace events */ 44 /* Callback when a probe point is found */
38 int max_tevs; /* Max number of trace events */ 45 int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
39 46
40 /* For function searching */ 47 /* For function searching */
41 int lno; /* Line number */ 48 int lno; /* Line number */
@@ -53,6 +60,22 @@ struct probe_finder {
53 struct probe_trace_arg *tvar; /* Current result variable */ 60 struct probe_trace_arg *tvar; /* Current result variable */
54}; 61};
55 62
63struct trace_event_finder {
64 struct probe_finder pf;
65 struct probe_trace_event *tevs; /* Found trace events */
66 int ntevs; /* Number of trace events */
67 int max_tevs; /* Max number of trace events */
68};
69
70struct available_var_finder {
71 struct probe_finder pf;
72 struct variable_list *vls; /* Found variable lists */
73 int nvls; /* Number of variable lists */
74 int max_vls; /* Max no. of variable lists */
75 bool externs; /* Find external vars too */
76 bool child; /* Search child scopes */
77};
78
56struct line_finder { 79struct line_finder {
57 struct line_range *lr; /* Target line range */ 80 struct line_range *lr; /* Target line range */
58 81
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
index 5ad07023504b..4cedea59f518 100644
--- a/tools/perf/util/pstack.h
+++ b/tools/perf/util/pstack.h
@@ -1,6 +1,8 @@
1#ifndef _PERF_PSTACK_ 1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_ 2#define _PERF_PSTACK_
3 3
4#include <stdbool.h>
5
4struct pstack; 6struct pstack;
5struct pstack *pstack__new(unsigned short max_nr_entries); 7struct pstack *pstack__new(unsigned short max_nr_entries);
6void pstack__delete(struct pstack *self); 8void pstack__delete(struct pstack *self);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 1c61a4f4aa8a..b62a553cc67d 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -196,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
196 196
197 if (verbose) { 197 if (verbose) {
198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
199 ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); 199 ret += repsep_snprintf(bf, size, "%*Lx %c ",
200 BITS_PER_LONG / 4, self->ip, o);
200 } 201 }
201 202
202 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 203 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
@@ -204,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
204 ret += repsep_snprintf(bf + ret, size - ret, "%s", 205 ret += repsep_snprintf(bf + ret, size - ret, "%s",
205 self->ms.sym->name); 206 self->ms.sym->name);
206 else 207 else
207 ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); 208 ret += repsep_snprintf(bf + ret, size - ret, "%*Lx",
209 BITS_PER_LONG / 4, self->ip);
208 210
209 return ret; 211 return ret;
210} 212}
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 46e531d09e8b..0b91053a7d11 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -70,7 +70,7 @@ struct hist_entry {
70 struct hist_entry *pair; 70 struct hist_entry *pair;
71 struct rb_root sorted_chain; 71 struct rb_root sorted_chain;
72 }; 72 };
73 struct callchain_node callchain[0]; 73 struct callchain_root callchain[0];
74}; 74};
75 75
76enum sort_type { 76enum sort_type {
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 0409fc7c0058..8fc0bd3a3a4a 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -259,7 +259,7 @@ static bool __match_glob(const char *str, const char *pat, bool ignore_space)
259 if (!*pat) /* Tail wild card matches all */ 259 if (!*pat) /* Tail wild card matches all */
260 return true; 260 return true;
261 while (*str) 261 while (*str)
262 if (strglobmatch(str++, pat)) 262 if (__match_glob(str++, pat, ignore_space))
263 return true; 263 return true;
264 } 264 }
265 return !*str && !*pat; 265 return !*str && !*pat;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 6f0dd90c36ce..439ab947daf4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -131,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self)
131 __map_groups__fixup_end(self, i); 131 __map_groups__fixup_end(self, i);
132} 132}
133 133
134static struct symbol *symbol__new(u64 start, u64 len, const char *name) 134static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
135 const char *name)
135{ 136{
136 size_t namelen = strlen(name) + 1; 137 size_t namelen = strlen(name) + 1;
137 struct symbol *self = calloc(1, (symbol_conf.priv_size + 138 struct symbol *self = calloc(1, (symbol_conf.priv_size +
@@ -144,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
144 145
145 self->start = start; 146 self->start = start;
146 self->end = len ? start + len - 1 : start; 147 self->end = len ? start + len - 1 : start;
148 self->binding = binding;
147 self->namelen = namelen - 1; 149 self->namelen = namelen - 1;
148 150
149 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); 151 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
@@ -160,8 +162,11 @@ void symbol__delete(struct symbol *self)
160 162
161static size_t symbol__fprintf(struct symbol *self, FILE *fp) 163static size_t symbol__fprintf(struct symbol *self, FILE *fp)
162{ 164{
163 return fprintf(fp, " %llx-%llx %s\n", 165 return fprintf(fp, " %llx-%llx %c %s\n",
164 self->start, self->end, self->name); 166 self->start, self->end,
167 self->binding == STB_GLOBAL ? 'g' :
168 self->binding == STB_LOCAL ? 'l' : 'w',
169 self->name);
165} 170}
166 171
167void dso__set_long_name(struct dso *self, char *name) 172void dso__set_long_name(struct dso *self, char *name)
@@ -290,7 +295,9 @@ static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym)
290{ 295{
291 struct rb_node **p = &self->rb_node; 296 struct rb_node **p = &self->rb_node;
292 struct rb_node *parent = NULL; 297 struct rb_node *parent = NULL;
293 struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; 298 struct symbol_name_rb_node *symn, *s;
299
300 symn = container_of(sym, struct symbol_name_rb_node, sym);
294 301
295 while (*p != NULL) { 302 while (*p != NULL) {
296 parent = *p; 303 parent = *p;
@@ -383,6 +390,20 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
383 return fprintf(fp, "%s", sbuild_id); 390 return fprintf(fp, "%s", sbuild_id);
384} 391}
385 392
393size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
394{
395 size_t ret = 0;
396 struct rb_node *nd;
397 struct symbol_name_rb_node *pos;
398
399 for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
400 pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
401 fprintf(fp, "%s\n", pos->sym.name);
402 }
403
404 return ret;
405}
406
386size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) 407size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
387{ 408{
388 struct rb_node *nd; 409 struct rb_node *nd;
@@ -453,6 +474,14 @@ struct process_kallsyms_args {
453 struct dso *dso; 474 struct dso *dso;
454}; 475};
455 476
477static u8 kallsyms2elf_type(char type)
478{
479 if (type == 'W')
480 return STB_WEAK;
481
482 return isupper(type) ? STB_GLOBAL : STB_LOCAL;
483}
484
456static int map__process_kallsym_symbol(void *arg, const char *name, 485static int map__process_kallsym_symbol(void *arg, const char *name,
457 char type, u64 start) 486 char type, u64 start)
458{ 487{
@@ -466,7 +495,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
466 /* 495 /*
467 * Will fix up the end later, when we have all symbols sorted. 496 * Will fix up the end later, when we have all symbols sorted.
468 */ 497 */
469 sym = symbol__new(start, 0, name); 498 sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
470 499
471 if (sym == NULL) 500 if (sym == NULL)
472 return -ENOMEM; 501 return -ENOMEM;
@@ -503,7 +532,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
503 struct machine *machine = kmaps->machine; 532 struct machine *machine = kmaps->machine;
504 struct map *curr_map = map; 533 struct map *curr_map = map;
505 struct symbol *pos; 534 struct symbol *pos;
506 int count = 0; 535 int count = 0, moved = 0;
507 struct rb_root *root = &self->symbols[map->type]; 536 struct rb_root *root = &self->symbols[map->type];
508 struct rb_node *next = rb_first(root); 537 struct rb_node *next = rb_first(root);
509 int kernel_range = 0; 538 int kernel_range = 0;
@@ -561,6 +590,11 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
561 char dso_name[PATH_MAX]; 590 char dso_name[PATH_MAX];
562 struct dso *dso; 591 struct dso *dso;
563 592
593 if (count == 0) {
594 curr_map = map;
595 goto filter_symbol;
596 }
597
564 if (self->kernel == DSO_TYPE_GUEST_KERNEL) 598 if (self->kernel == DSO_TYPE_GUEST_KERNEL)
565 snprintf(dso_name, sizeof(dso_name), 599 snprintf(dso_name, sizeof(dso_name),
566 "[guest.kernel].%d", 600 "[guest.kernel].%d",
@@ -586,7 +620,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map,
586 map_groups__insert(kmaps, curr_map); 620 map_groups__insert(kmaps, curr_map);
587 ++kernel_range; 621 ++kernel_range;
588 } 622 }
589 623filter_symbol:
590 if (filter && filter(curr_map, pos)) { 624 if (filter && filter(curr_map, pos)) {
591discard_symbol: rb_erase(&pos->rb_node, root); 625discard_symbol: rb_erase(&pos->rb_node, root);
592 symbol__delete(pos); 626 symbol__delete(pos);
@@ -594,8 +628,9 @@ discard_symbol: rb_erase(&pos->rb_node, root);
594 if (curr_map != map) { 628 if (curr_map != map) {
595 rb_erase(&pos->rb_node, root); 629 rb_erase(&pos->rb_node, root);
596 symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); 630 symbols__insert(&curr_map->dso->symbols[curr_map->type], pos);
597 } 631 ++moved;
598 count++; 632 } else
633 ++count;
599 } 634 }
600 } 635 }
601 636
@@ -605,7 +640,7 @@ discard_symbol: rb_erase(&pos->rb_node, root);
605 dso__set_loaded(curr_map->dso, curr_map->type); 640 dso__set_loaded(curr_map->dso, curr_map->type);
606 } 641 }
607 642
608 return count; 643 return count + moved;
609} 644}
610 645
611int dso__load_kallsyms(struct dso *self, const char *filename, 646int dso__load_kallsyms(struct dso *self, const char *filename,
@@ -661,7 +696,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
661 if (len + 2 >= line_len) 696 if (len + 2 >= line_len)
662 continue; 697 continue;
663 698
664 sym = symbol__new(start, size, line + len); 699 sym = symbol__new(start, size, STB_GLOBAL, line + len);
665 700
666 if (sym == NULL) 701 if (sym == NULL)
667 goto out_delete_line; 702 goto out_delete_line;
@@ -873,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
873 "%s@plt", elf_sym__name(&sym, symstrs)); 908 "%s@plt", elf_sym__name(&sym, symstrs));
874 909
875 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 910 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
876 sympltname); 911 STB_GLOBAL, sympltname);
877 if (!f) 912 if (!f)
878 goto out_elf_end; 913 goto out_elf_end;
879 914
@@ -895,7 +930,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
895 "%s@plt", elf_sym__name(&sym, symstrs)); 930 "%s@plt", elf_sym__name(&sym, symstrs));
896 931
897 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 932 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
898 sympltname); 933 STB_GLOBAL, sympltname);
899 if (!f) 934 if (!f)
900 goto out_elf_end; 935 goto out_elf_end;
901 936
@@ -1066,6 +1101,16 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1066 if (!is_label && !elf_sym__is_a(&sym, map->type)) 1101 if (!is_label && !elf_sym__is_a(&sym, map->type))
1067 continue; 1102 continue;
1068 1103
1104 /* Reject ARM ELF "mapping symbols": these aren't unique and
1105 * don't identify functions, so will confuse the profile
1106 * output: */
1107 if (ehdr.e_machine == EM_ARM) {
1108 if (!strcmp(elf_name, "$a") ||
1109 !strcmp(elf_name, "$d") ||
1110 !strcmp(elf_name, "$t"))
1111 continue;
1112 }
1113
1069 if (opdsec && sym.st_shndx == opdidx) { 1114 if (opdsec && sym.st_shndx == opdidx) {
1070 u32 offset = sym.st_value - opdshdr.sh_addr; 1115 u32 offset = sym.st_value - opdshdr.sh_addr;
1071 u64 *opd = opddata->d_buf + offset; 1116 u64 *opd = opddata->d_buf + offset;
@@ -1146,7 +1191,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1146 if (demangled != NULL) 1191 if (demangled != NULL)
1147 elf_name = demangled; 1192 elf_name = demangled;
1148new_symbol: 1193new_symbol:
1149 f = symbol__new(sym.st_value, sym.st_size, elf_name); 1194 f = symbol__new(sym.st_value, sym.st_size,
1195 GELF_ST_BIND(sym.st_info), elf_name);
1150 free(demangled); 1196 free(demangled);
1151 if (!f) 1197 if (!f)
1152 goto out_elf_end; 1198 goto out_elf_end;
@@ -1734,8 +1780,8 @@ out_failure:
1734 return -1; 1780 return -1;
1735} 1781}
1736 1782
1737static int dso__load_vmlinux(struct dso *self, struct map *map, 1783int dso__load_vmlinux(struct dso *self, struct map *map,
1738 const char *vmlinux, symbol_filter_t filter) 1784 const char *vmlinux, symbol_filter_t filter)
1739{ 1785{
1740 int err = -1, fd; 1786 int err = -1, fd;
1741 1787
@@ -2085,14 +2131,55 @@ static struct dso *machine__create_kernel(struct machine *self)
2085 return kernel; 2131 return kernel;
2086} 2132}
2087 2133
2134struct process_args {
2135 u64 start;
2136};
2137
2138static int symbol__in_kernel(void *arg, const char *name,
2139 char type __used, u64 start)
2140{
2141 struct process_args *args = arg;
2142
2143 if (strchr(name, '['))
2144 return 0;
2145
2146 args->start = start;
2147 return 1;
2148}
2149
2150/* Figure out the start address of kernel map from /proc/kallsyms */
2151static u64 machine__get_kernel_start_addr(struct machine *machine)
2152{
2153 const char *filename;
2154 char path[PATH_MAX];
2155 struct process_args args;
2156
2157 if (machine__is_host(machine)) {
2158 filename = "/proc/kallsyms";
2159 } else {
2160 if (machine__is_default_guest(machine))
2161 filename = (char *)symbol_conf.default_guest_kallsyms;
2162 else {
2163 sprintf(path, "%s/proc/kallsyms", machine->root_dir);
2164 filename = path;
2165 }
2166 }
2167
2168 if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0)
2169 return 0;
2170
2171 return args.start;
2172}
2173
2088int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) 2174int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
2089{ 2175{
2090 enum map_type type; 2176 enum map_type type;
2177 u64 start = machine__get_kernel_start_addr(self);
2091 2178
2092 for (type = 0; type < MAP__NR_TYPES; ++type) { 2179 for (type = 0; type < MAP__NR_TYPES; ++type) {
2093 struct kmap *kmap; 2180 struct kmap *kmap;
2094 2181
2095 self->vmlinux_maps[type] = map__new2(0, kernel, type); 2182 self->vmlinux_maps[type] = map__new2(start, kernel, type);
2096 if (self->vmlinux_maps[type] == NULL) 2183 if (self->vmlinux_maps[type] == NULL)
2097 return -1; 2184 return -1;
2098 2185
@@ -2244,6 +2331,9 @@ static int setup_list(struct strlist **list, const char *list_str,
2244 2331
2245int symbol__init(void) 2332int symbol__init(void)
2246{ 2333{
2334 if (symbol_conf.initialized)
2335 return 0;
2336
2247 elf_version(EV_CURRENT); 2337 elf_version(EV_CURRENT);
2248 if (symbol_conf.sort_by_name) 2338 if (symbol_conf.sort_by_name)
2249 symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - 2339 symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
@@ -2269,6 +2359,7 @@ int symbol__init(void)
2269 symbol_conf.sym_list_str, "symbol") < 0) 2359 symbol_conf.sym_list_str, "symbol") < 0)
2270 goto out_free_comm_list; 2360 goto out_free_comm_list;
2271 2361
2362 symbol_conf.initialized = true;
2272 return 0; 2363 return 0;
2273 2364
2274out_free_dso_list: 2365out_free_dso_list:
@@ -2280,11 +2371,14 @@ out_free_comm_list:
2280 2371
2281void symbol__exit(void) 2372void symbol__exit(void)
2282{ 2373{
2374 if (!symbol_conf.initialized)
2375 return;
2283 strlist__delete(symbol_conf.sym_list); 2376 strlist__delete(symbol_conf.sym_list);
2284 strlist__delete(symbol_conf.dso_list); 2377 strlist__delete(symbol_conf.dso_list);
2285 strlist__delete(symbol_conf.comm_list); 2378 strlist__delete(symbol_conf.comm_list);
2286 vmlinux_path__exit(); 2379 vmlinux_path__exit();
2287 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; 2380 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
2381 symbol_conf.initialized = false;
2288} 2382}
2289 2383
2290int machines__create_kernel_maps(struct rb_root *self, pid_t pid) 2384int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 906be20011d9..6c6eafdb932d 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -53,6 +53,7 @@ struct symbol {
53 u64 start; 53 u64 start;
54 u64 end; 54 u64 end;
55 u16 namelen; 55 u16 namelen;
56 u8 binding;
56 char name[0]; 57 char name[0];
57}; 58};
58 59
@@ -68,7 +69,8 @@ struct symbol_conf {
68 show_nr_samples, 69 show_nr_samples,
69 use_callchain, 70 use_callchain,
70 exclude_other, 71 exclude_other,
71 show_cpu_utilization; 72 show_cpu_utilization,
73 initialized;
72 const char *vmlinux_name, 74 const char *vmlinux_name,
73 *source_prefix, 75 *source_prefix,
74 *field_sep; 76 *field_sep;
@@ -164,6 +166,8 @@ void dso__sort_by_name(struct dso *self, enum map_type type);
164struct dso *__dsos__findnew(struct list_head *head, const char *name); 166struct dso *__dsos__findnew(struct list_head *head, const char *name);
165 167
166int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); 168int dso__load(struct dso *self, struct map *map, symbol_filter_t filter);
169int dso__load_vmlinux(struct dso *self, struct map *map,
170 const char *vmlinux, symbol_filter_t filter);
167int dso__load_vmlinux_path(struct dso *self, struct map *map, 171int dso__load_vmlinux_path(struct dso *self, struct map *map,
168 symbol_filter_t filter); 172 symbol_filter_t filter);
169int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, 173int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map,
@@ -180,6 +184,7 @@ size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
180size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); 184size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
181 185
182size_t dso__fprintf_buildid(struct dso *self, FILE *fp); 186size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
187size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
183size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); 188size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
184 189
185enum dso_origin { 190enum dso_origin {
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 7ea983acfaea..f7af2fca965d 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -97,7 +97,7 @@ void setup_python_scripting(void)
97 register_python_scripting(&python_scripting_unsupported_ops); 97 register_python_scripting(&python_scripting_unsupported_ops);
98} 98}
99#else 99#else
100struct scripting_ops python_scripting_ops; 100extern struct scripting_ops python_scripting_ops;
101 101
102void setup_python_scripting(void) 102void setup_python_scripting(void)
103{ 103{
@@ -158,7 +158,7 @@ void setup_perl_scripting(void)
158 register_perl_scripting(&perl_scripting_unsupported_ops); 158 register_perl_scripting(&perl_scripting_unsupported_ops);
159} 159}
160#else 160#else
161struct scripting_ops perl_scripting_ops; 161extern struct scripting_ops perl_scripting_ops;
162 162
163void setup_perl_scripting(void) 163void setup_perl_scripting(void)
164{ 164{
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 000000000000..8bc010edca25
--- /dev/null
+++ b/tools/perf/util/ui/browser.c
@@ -0,0 +1,337 @@
1#include "libslang.h"
2#include <linux/compiler.h>
3#include <linux/list.h>
4#include <linux/rbtree.h>
5#include <stdlib.h>
6#include <sys/ttydefaults.h>
7#include "browser.h"
8#include "helpline.h"
9#include "../color.h"
10#include "../util.h"
11#include <stdio.h>
12
13static int ui_browser__percent_color(double percent, bool current)
14{
15 if (current)
16 return HE_COLORSET_SELECTED;
17 if (percent >= MIN_RED)
18 return HE_COLORSET_TOP;
19 if (percent >= MIN_GREEN)
20 return HE_COLORSET_MEDIUM;
21 return HE_COLORSET_NORMAL;
22}
23
24void ui_browser__set_color(struct ui_browser *self __used, int color)
25{
26 SLsmg_set_color(color);
27}
28
29void ui_browser__set_percent_color(struct ui_browser *self,
30 double percent, bool current)
31{
32 int color = ui_browser__percent_color(percent, current);
33 ui_browser__set_color(self, color);
34}
35
36void ui_browser__gotorc(struct ui_browser *self, int y, int x)
37{
38 SLsmg_gotorc(self->y + y, self->x + x);
39}
40
41void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
42{
43 struct list_head *head = self->entries;
44 struct list_head *pos;
45
46 switch (whence) {
47 case SEEK_SET:
48 pos = head->next;
49 break;
50 case SEEK_CUR:
51 pos = self->top;
52 break;
53 case SEEK_END:
54 pos = head->prev;
55 break;
56 default:
57 return;
58 }
59
60 if (offset > 0) {
61 while (offset-- != 0)
62 pos = pos->next;
63 } else {
64 while (offset++ != 0)
65 pos = pos->prev;
66 }
67
68 self->top = pos;
69}
70
71void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
72{
73 struct rb_root *root = self->entries;
74 struct rb_node *nd;
75
76 switch (whence) {
77 case SEEK_SET:
78 nd = rb_first(root);
79 break;
80 case SEEK_CUR:
81 nd = self->top;
82 break;
83 case SEEK_END:
84 nd = rb_last(root);
85 break;
86 default:
87 return;
88 }
89
90 if (offset > 0) {
91 while (offset-- != 0)
92 nd = rb_next(nd);
93 } else {
94 while (offset++ != 0)
95 nd = rb_prev(nd);
96 }
97
98 self->top = nd;
99}
100
101unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
102{
103 struct rb_node *nd;
104 int row = 0;
105
106 if (self->top == NULL)
107 self->top = rb_first(self->entries);
108
109 nd = self->top;
110
111 while (nd != NULL) {
112 ui_browser__gotorc(self, row, 0);
113 self->write(self, nd, row);
114 if (++row == self->height)
115 break;
116 nd = rb_next(nd);
117 }
118
119 return row;
120}
121
122bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
123{
124 return self->top_idx + row == self->index;
125}
126
127void ui_browser__refresh_dimensions(struct ui_browser *self)
128{
129 int cols, rows;
130 newtGetScreenSize(&cols, &rows);
131
132 self->width = cols - 1;
133 self->height = rows - 2;
134 self->y = 1;
135 self->x = 0;
136}
137
138void ui_browser__reset_index(struct ui_browser *self)
139{
140 self->index = self->top_idx = 0;
141 self->seek(self, 0, SEEK_SET);
142}
143
144void ui_browser__add_exit_key(struct ui_browser *self, int key)
145{
146 newtFormAddHotKey(self->form, key);
147}
148
149void ui_browser__add_exit_keys(struct ui_browser *self, int keys[])
150{
151 int i = 0;
152
153 while (keys[i] && i < 64) {
154 ui_browser__add_exit_key(self, keys[i]);
155 ++i;
156 }
157}
158
159int ui_browser__show(struct ui_browser *self, const char *title,
160 const char *helpline, ...)
161{
162 va_list ap;
163 int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP,
164 NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ',
165 NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 };
166
167 if (self->form != NULL)
168 newtFormDestroy(self->form);
169
170 ui_browser__refresh_dimensions(self);
171 self->form = newtForm(NULL, NULL, 0);
172 if (self->form == NULL)
173 return -1;
174
175 self->sb = newtVerticalScrollbar(self->width, 1, self->height,
176 HE_COLORSET_NORMAL,
177 HE_COLORSET_SELECTED);
178 if (self->sb == NULL)
179 return -1;
180
181 SLsmg_gotorc(0, 0);
182 ui_browser__set_color(self, NEWT_COLORSET_ROOT);
183 slsmg_write_nstring(title, self->width);
184
185 ui_browser__add_exit_keys(self, keys);
186 newtFormAddComponent(self->form, self->sb);
187
188 va_start(ap, helpline);
189 ui_helpline__vpush(helpline, ap);
190 va_end(ap);
191 return 0;
192}
193
194void ui_browser__hide(struct ui_browser *self)
195{
196 newtFormDestroy(self->form);
197 self->form = NULL;
198 ui_helpline__pop();
199}
200
201int ui_browser__refresh(struct ui_browser *self)
202{
203 int row;
204
205 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
206 row = self->refresh(self);
207 ui_browser__set_color(self, HE_COLORSET_NORMAL);
208 SLsmg_fill_region(self->y + row, self->x,
209 self->height - row, self->width, ' ');
210
211 return 0;
212}
213
214int ui_browser__run(struct ui_browser *self)
215{
216 struct newtExitStruct es;
217
218 if (ui_browser__refresh(self) < 0)
219 return -1;
220
221 while (1) {
222 off_t offset;
223
224 newtFormRun(self->form, &es);
225
226 if (es.reason != NEWT_EXIT_HOTKEY)
227 break;
228 switch (es.u.key) {
229 case NEWT_KEY_DOWN:
230 if (self->index == self->nr_entries - 1)
231 break;
232 ++self->index;
233 if (self->index == self->top_idx + self->height) {
234 ++self->top_idx;
235 self->seek(self, +1, SEEK_CUR);
236 }
237 break;
238 case NEWT_KEY_UP:
239 if (self->index == 0)
240 break;
241 --self->index;
242 if (self->index < self->top_idx) {
243 --self->top_idx;
244 self->seek(self, -1, SEEK_CUR);
245 }
246 break;
247 case NEWT_KEY_PGDN:
248 case ' ':
249 if (self->top_idx + self->height > self->nr_entries - 1)
250 break;
251
252 offset = self->height;
253 if (self->index + offset > self->nr_entries - 1)
254 offset = self->nr_entries - 1 - self->index;
255 self->index += offset;
256 self->top_idx += offset;
257 self->seek(self, +offset, SEEK_CUR);
258 break;
259 case NEWT_KEY_PGUP:
260 if (self->top_idx == 0)
261 break;
262
263 if (self->top_idx < self->height)
264 offset = self->top_idx;
265 else
266 offset = self->height;
267
268 self->index -= offset;
269 self->top_idx -= offset;
270 self->seek(self, -offset, SEEK_CUR);
271 break;
272 case NEWT_KEY_HOME:
273 ui_browser__reset_index(self);
274 break;
275 case NEWT_KEY_END:
276 offset = self->height - 1;
277 if (offset >= self->nr_entries)
278 offset = self->nr_entries - 1;
279
280 self->index = self->nr_entries - 1;
281 self->top_idx = self->index - offset;
282 self->seek(self, -offset, SEEK_END);
283 break;
284 default:
285 return es.u.key;
286 }
287 if (ui_browser__refresh(self) < 0)
288 return -1;
289 }
290 return -1;
291}
292
293unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
294{
295 struct list_head *pos;
296 struct list_head *head = self->entries;
297 int row = 0;
298
299 if (self->top == NULL || self->top == self->entries)
300 self->top = head->next;
301
302 pos = self->top;
303
304 list_for_each_from(pos, head) {
305 ui_browser__gotorc(self, row, 0);
306 self->write(self, pos, row);
307 if (++row == self->height)
308 break;
309 }
310
311 return row;
312}
313
314static struct newtPercentTreeColors {
315 const char *topColorFg, *topColorBg;
316 const char *mediumColorFg, *mediumColorBg;
317 const char *normalColorFg, *normalColorBg;
318 const char *selColorFg, *selColorBg;
319 const char *codeColorFg, *codeColorBg;
320} defaultPercentTreeColors = {
321 "red", "lightgray",
322 "green", "lightgray",
323 "black", "lightgray",
324 "lightgray", "magenta",
325 "blue", "lightgray",
326};
327
328void ui_browser__init(void)
329{
330 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
331
332 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
333 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
334 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
335 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
336 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
337}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
new file mode 100644
index 000000000000..0dc7e4da36f5
--- /dev/null
+++ b/tools/perf/util/ui/browser.h
@@ -0,0 +1,51 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h>
7#include "../types.h"
8
9#define HE_COLORSET_TOP 50
10#define HE_COLORSET_MEDIUM 51
11#define HE_COLORSET_NORMAL 52
12#define HE_COLORSET_SELECTED 53
13#define HE_COLORSET_CODE 54
14
15struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx;
18 void *top, *entries;
19 u16 y, x, width, height;
20 void *priv;
21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 u32 nr_entries;
25};
26
27
28void ui_browser__set_color(struct ui_browser *self, int color);
29void ui_browser__set_percent_color(struct ui_browser *self,
30 double percent, bool current);
31bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
32void ui_browser__refresh_dimensions(struct ui_browser *self);
33void ui_browser__reset_index(struct ui_browser *self);
34
35void ui_browser__gotorc(struct ui_browser *self, int y, int x);
36void ui_browser__add_exit_key(struct ui_browser *self, int key);
37void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]);
38int ui_browser__show(struct ui_browser *self, const char *title,
39 const char *helpline, ...);
40void ui_browser__hide(struct ui_browser *self);
41int ui_browser__refresh(struct ui_browser *self);
42int ui_browser__run(struct ui_browser *self);
43
44void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
45unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
46
47void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
48unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
49
50void ui_browser__init(void);
51#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 000000000000..82b78f99251b
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,237 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../hist.h"
5#include "../../sort.h"
6#include "../../symbol.h"
7
8static void ui__error_window(const char *fmt, ...)
9{
10 va_list ap;
11
12 va_start(ap, fmt);
13 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
14 va_end(ap);
15}
16
17struct annotate_browser {
18 struct ui_browser b;
19 struct rb_root entries;
20 struct rb_node *curr_hot;
21};
22
23struct objdump_line_rb_node {
24 struct rb_node rb_node;
25 double percent;
26 u32 idx;
27};
28
29static inline
30struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
31{
32 return (struct objdump_line_rb_node *)(self + 1);
33}
34
35static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
36{
37 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
38 bool current_entry = ui_browser__is_current_entry(self, row);
39 int width = self->width;
40
41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 ui_browser__set_percent_color(self, olrb->percent, current_entry);
44 slsmg_printf(" %7.2f ", olrb->percent);
45 if (!current_entry)
46 ui_browser__set_color(self, HE_COLORSET_CODE);
47 } else {
48 ui_browser__set_percent_color(self, 0, current_entry);
49 slsmg_write_nstring(" ", 9);
50 }
51
52 SLsmg_write_char(':');
53 slsmg_write_nstring(" ", 8);
54 if (!*ol->line)
55 slsmg_write_nstring(" ", width - 18);
56 else
57 slsmg_write_nstring(ol->line, width - 18);
58}
59
60static double objdump_line__calc_percent(struct objdump_line *self,
61 struct list_head *head,
62 struct symbol *sym)
63{
64 double percent = 0.0;
65
66 if (self->offset != -1) {
67 int len = sym->end - sym->start;
68 unsigned int hits = 0;
69 struct sym_priv *priv = symbol__priv(sym);
70 struct sym_ext *sym_ext = priv->ext;
71 struct sym_hist *h = priv->hist;
72 s64 offset = self->offset;
73 struct objdump_line *next = objdump__get_next_ip_line(head, self);
74
75
76 while (offset < (s64)len &&
77 (next == NULL || offset < next->offset)) {
78 if (sym_ext) {
79 percent += sym_ext[offset].percent;
80 } else
81 hits += h->ip[offset];
82
83 ++offset;
84 }
85
86 if (sym_ext == NULL && h->sum)
87 percent = 100.0 * hits / h->sum;
88 }
89
90 return percent;
91}
92
93static void objdump__insert_line(struct rb_root *self,
94 struct objdump_line_rb_node *line)
95{
96 struct rb_node **p = &self->rb_node;
97 struct rb_node *parent = NULL;
98 struct objdump_line_rb_node *l;
99
100 while (*p != NULL) {
101 parent = *p;
102 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
103 if (line->percent < l->percent)
104 p = &(*p)->rb_left;
105 else
106 p = &(*p)->rb_right;
107 }
108 rb_link_node(&line->rb_node, parent, p);
109 rb_insert_color(&line->rb_node, self);
110}
111
112static void annotate_browser__set_top(struct annotate_browser *self,
113 struct rb_node *nd)
114{
115 struct objdump_line_rb_node *rbpos;
116 struct objdump_line *pos;
117 unsigned back;
118
119 ui_browser__refresh_dimensions(&self->b);
120 back = self->b.height / 2;
121 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
122 pos = ((struct objdump_line *)rbpos) - 1;
123 self->b.top_idx = self->b.index = rbpos->idx;
124
125 while (self->b.top_idx != 0 && back != 0) {
126 pos = list_entry(pos->node.prev, struct objdump_line, node);
127
128 --self->b.top_idx;
129 --back;
130 }
131
132 self->b.top = pos;
133 self->curr_hot = nd;
134}
135
136static int annotate_browser__run(struct annotate_browser *self)
137{
138 struct rb_node *nd;
139 struct hist_entry *he = self->b.priv;
140 int key;
141
142 if (ui_browser__show(&self->b, he->ms.sym->name,
143 "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
144 return -1;
145 /*
146 * To allow builtin-annotate to cycle thru multiple symbols by
147 * examining the exit key for this function.
148 */
149 ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT);
150
151 nd = self->curr_hot;
152 if (nd) {
153 int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 };
154 ui_browser__add_exit_keys(&self->b, tabs);
155 }
156
157 while (1) {
158 key = ui_browser__run(&self->b);
159
160 switch (key) {
161 case NEWT_KEY_TAB:
162 nd = rb_prev(nd);
163 if (nd == NULL)
164 nd = rb_last(&self->entries);
165 annotate_browser__set_top(self, nd);
166 break;
167 case NEWT_KEY_UNTAB:
168 nd = rb_next(nd);
169 if (nd == NULL)
170 nd = rb_first(&self->entries);
171 annotate_browser__set_top(self, nd);
172 break;
173 default:
174 goto out;
175 }
176 }
177out:
178 ui_browser__hide(&self->b);
179 return key;
180}
181
182int hist_entry__tui_annotate(struct hist_entry *self)
183{
184 struct objdump_line *pos, *n;
185 struct objdump_line_rb_node *rbpos;
186 LIST_HEAD(head);
187 struct annotate_browser browser = {
188 .b = {
189 .entries = &head,
190 .refresh = ui_browser__list_head_refresh,
191 .seek = ui_browser__list_head_seek,
192 .write = annotate_browser__write,
193 .priv = self,
194 },
195 };
196 int ret;
197
198 if (self->ms.sym == NULL)
199 return -1;
200
201 if (self->ms.map->dso->annotate_warned)
202 return -1;
203
204 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
205 ui__error_window(ui_helpline__last_msg);
206 return -1;
207 }
208
209 ui_helpline__push("Press <- or ESC to exit");
210
211 list_for_each_entry(pos, &head, node) {
212 size_t line_len = strlen(pos->line);
213 if (browser.b.width < line_len)
214 browser.b.width = line_len;
215 rbpos = objdump_line__rb(pos);
216 rbpos->idx = browser.b.nr_entries++;
217 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
218 if (rbpos->percent < 0.01)
219 continue;
220 objdump__insert_line(&browser.entries, rbpos);
221 }
222
223 /*
224 * Position the browser at the hottest line.
225 */
226 browser.curr_hot = rb_last(&browser.entries);
227 if (browser.curr_hot)
228 annotate_browser__set_top(&browser, browser.curr_hot);
229
230 browser.b.width += 18; /* Percentage */
231 ret = annotate_browser__run(&browser);
232 list_for_each_entry_safe(pos, n, &head, node) {
233 list_del(&pos->node);
234 objdump_line__free(pos);
235 }
236 return ret;
237}
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
new file mode 100644
index 000000000000..ebda8c3fde9e
--- /dev/null
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -0,0 +1,1013 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4#include "../libslang.h"
5#include <stdlib.h>
6#include <string.h>
7#include <newt.h>
8#include <linux/rbtree.h>
9
10#include "../../hist.h"
11#include "../../pstack.h"
12#include "../../sort.h"
13#include "../../util.h"
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
18#include "map.h"
19
20struct hist_browser {
21 struct ui_browser b;
22 struct hists *hists;
23 struct hist_entry *he_selection;
24 struct map_symbol *selection;
25};
26
27static void hist_browser__refresh_dimensions(struct hist_browser *self)
28{
29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
31 sizeof("[k]"));
32}
33
34static void hist_browser__reset(struct hist_browser *self)
35{
36 self->b.nr_entries = self->hists->nr_entries;
37 hist_browser__refresh_dimensions(self);
38 ui_browser__reset_index(&self->b);
39}
40
41static char tree__folded_sign(bool unfolded)
42{
43 return unfolded ? '-' : '+';
44}
45
46static char map_symbol__folded(const struct map_symbol *self)
47{
48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
49}
50
51static char hist_entry__folded(const struct hist_entry *self)
52{
53 return map_symbol__folded(&self->ms);
54}
55
56static char callchain_list__folded(const struct callchain_list *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
61static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
62{
63 self->unfolded = unfold ? self->has_children : false;
64}
65
66static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
67{
68 int n = 0;
69 struct rb_node *nd;
70
71 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
72 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
73 struct callchain_list *chain;
74 char folded_sign = ' '; /* No children */
75
76 list_for_each_entry(chain, &child->val, list) {
77 ++n;
78 /* We need this because we may not have children */
79 folded_sign = callchain_list__folded(chain);
80 if (folded_sign == '+')
81 break;
82 }
83
84 if (folded_sign == '-') /* Have children and they're unfolded */
85 n += callchain_node__count_rows_rb_tree(child);
86 }
87
88 return n;
89}
90
91static int callchain_node__count_rows(struct callchain_node *node)
92{
93 struct callchain_list *chain;
94 bool unfolded = false;
95 int n = 0;
96
97 list_for_each_entry(chain, &node->val, list) {
98 ++n;
99 unfolded = chain->ms.unfolded;
100 }
101
102 if (unfolded)
103 n += callchain_node__count_rows_rb_tree(node);
104
105 return n;
106}
107
108static int callchain__count_rows(struct rb_root *chain)
109{
110 struct rb_node *nd;
111 int n = 0;
112
113 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
114 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
115 n += callchain_node__count_rows(node);
116 }
117
118 return n;
119}
120
121static bool map_symbol__toggle_fold(struct map_symbol *self)
122{
123 if (!self->has_children)
124 return false;
125
126 self->unfolded = !self->unfolded;
127 return true;
128}
129
130static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
131{
132 struct rb_node *nd = rb_first(&self->rb_root);
133
134 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
135 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
136 struct callchain_list *chain;
137 bool first = true;
138
139 list_for_each_entry(chain, &child->val, list) {
140 if (first) {
141 first = false;
142 chain->ms.has_children = chain->list.next != &child->val ||
143 !RB_EMPTY_ROOT(&child->rb_root);
144 } else
145 chain->ms.has_children = chain->list.next == &child->val &&
146 !RB_EMPTY_ROOT(&child->rb_root);
147 }
148
149 callchain_node__init_have_children_rb_tree(child);
150 }
151}
152
153static void callchain_node__init_have_children(struct callchain_node *self)
154{
155 struct callchain_list *chain;
156
157 list_for_each_entry(chain, &self->val, list)
158 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
159
160 callchain_node__init_have_children_rb_tree(self);
161}
162
163static void callchain__init_have_children(struct rb_root *self)
164{
165 struct rb_node *nd;
166
167 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
168 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
169 callchain_node__init_have_children(node);
170 }
171}
172
173static void hist_entry__init_have_children(struct hist_entry *self)
174{
175 if (!self->init_have_children) {
176 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
177 callchain__init_have_children(&self->sorted_chain);
178 self->init_have_children = true;
179 }
180}
181
182static bool hist_browser__toggle_fold(struct hist_browser *self)
183{
184 if (map_symbol__toggle_fold(self->selection)) {
185 struct hist_entry *he = self->he_selection;
186
187 hist_entry__init_have_children(he);
188 self->hists->nr_entries -= he->nr_rows;
189
190 if (he->ms.unfolded)
191 he->nr_rows = callchain__count_rows(&he->sorted_chain);
192 else
193 he->nr_rows = 0;
194 self->hists->nr_entries += he->nr_rows;
195 self->b.nr_entries = self->hists->nr_entries;
196
197 return true;
198 }
199
200 /* If it doesn't have children, no toggling performed */
201 return false;
202}
203
204static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
205{
206 int n = 0;
207 struct rb_node *nd;
208
209 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
210 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
211 struct callchain_list *chain;
212 bool has_children = false;
213
214 list_for_each_entry(chain, &child->val, list) {
215 ++n;
216 map_symbol__set_folding(&chain->ms, unfold);
217 has_children = chain->ms.has_children;
218 }
219
220 if (has_children)
221 n += callchain_node__set_folding_rb_tree(child, unfold);
222 }
223
224 return n;
225}
226
227static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
228{
229 struct callchain_list *chain;
230 bool has_children = false;
231 int n = 0;
232
233 list_for_each_entry(chain, &node->val, list) {
234 ++n;
235 map_symbol__set_folding(&chain->ms, unfold);
236 has_children = chain->ms.has_children;
237 }
238
239 if (has_children)
240 n += callchain_node__set_folding_rb_tree(node, unfold);
241
242 return n;
243}
244
245static int callchain__set_folding(struct rb_root *chain, bool unfold)
246{
247 struct rb_node *nd;
248 int n = 0;
249
250 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
251 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
252 n += callchain_node__set_folding(node, unfold);
253 }
254
255 return n;
256}
257
258static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
259{
260 hist_entry__init_have_children(self);
261 map_symbol__set_folding(&self->ms, unfold);
262
263 if (self->ms.has_children) {
264 int n = callchain__set_folding(&self->sorted_chain, unfold);
265 self->nr_rows = unfold ? n : 0;
266 } else
267 self->nr_rows = 0;
268}
269
270static void hists__set_folding(struct hists *self, bool unfold)
271{
272 struct rb_node *nd;
273
274 self->nr_entries = 0;
275
276 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
277 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
278 hist_entry__set_folding(he, unfold);
279 self->nr_entries += 1 + he->nr_rows;
280 }
281}
282
283static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
284{
285 hists__set_folding(self->hists, unfold);
286 self->b.nr_entries = self->hists->nr_entries;
287 /* Go to the start, we may be way after valid entries after a collapse */
288 ui_browser__reset_index(&self->b);
289}
290
291static int hist_browser__run(struct hist_browser *self, const char *title)
292{
293 int key;
294 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
295 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
296
297 self->b.entries = &self->hists->entries;
298 self->b.nr_entries = self->hists->nr_entries;
299
300 hist_browser__refresh_dimensions(self);
301
302 if (ui_browser__show(&self->b, title,
303 "Press '?' for help on key bindings") < 0)
304 return -1;
305
306 ui_browser__add_exit_keys(&self->b, exit_keys);
307
308 while (1) {
309 key = ui_browser__run(&self->b);
310
311 switch (key) {
312 case 'D': { /* Debug */
313 static int seq;
314 struct hist_entry *h = rb_entry(self->b.top,
315 struct hist_entry, rb_node);
316 ui_helpline__pop();
317 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
318 seq++, self->b.nr_entries,
319 self->hists->nr_entries,
320 self->b.height,
321 self->b.index,
322 self->b.top_idx,
323 h->row_offset, h->nr_rows);
324 }
325 break;
326 case 'C':
327 /* Collapse the whole world. */
328 hist_browser__set_folding(self, false);
329 break;
330 case 'E':
331 /* Expand the whole world. */
332 hist_browser__set_folding(self, true);
333 break;
334 case NEWT_KEY_ENTER:
335 if (hist_browser__toggle_fold(self))
336 break;
337 /* fall thru */
338 default:
339 goto out;
340 }
341 }
342out:
343 ui_browser__hide(&self->b);
344 return key;
345}
346
347static char *callchain_list__sym_name(struct callchain_list *self,
348 char *bf, size_t bfsize)
349{
350 if (self->ms.sym)
351 return self->ms.sym->name;
352
353 snprintf(bf, bfsize, "%#Lx", self->ip);
354 return bf;
355}
356
357#define LEVEL_OFFSET_STEP 3
358
359static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
360 struct callchain_node *chain_node,
361 u64 total, int level,
362 unsigned short row,
363 off_t *row_offset,
364 bool *is_current_entry)
365{
366 struct rb_node *node;
367 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
368 u64 new_total, remaining;
369
370 if (callchain_param.mode == CHAIN_GRAPH_REL)
371 new_total = chain_node->children_hit;
372 else
373 new_total = total;
374
375 remaining = new_total;
376 node = rb_first(&chain_node->rb_root);
377 while (node) {
378 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
379 struct rb_node *next = rb_next(node);
380 u64 cumul = cumul_hits(child);
381 struct callchain_list *chain;
382 char folded_sign = ' ';
383 int first = true;
384 int extra_offset = 0;
385
386 remaining -= cumul;
387
388 list_for_each_entry(chain, &child->val, list) {
389 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
390 const char *str;
391 int color;
392 bool was_first = first;
393
394 if (first)
395 first = false;
396 else
397 extra_offset = LEVEL_OFFSET_STEP;
398
399 folded_sign = callchain_list__folded(chain);
400 if (*row_offset != 0) {
401 --*row_offset;
402 goto do_next;
403 }
404
405 alloc_str = NULL;
406 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
407 if (was_first) {
408 double percent = cumul * 100.0 / new_total;
409
410 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
411 str = "Not enough memory!";
412 else
413 str = alloc_str;
414 }
415
416 color = HE_COLORSET_NORMAL;
417 width = self->b.width - (offset + extra_offset + 2);
418 if (ui_browser__is_current_entry(&self->b, row)) {
419 self->selection = &chain->ms;
420 color = HE_COLORSET_SELECTED;
421 *is_current_entry = true;
422 }
423
424 ui_browser__set_color(&self->b, color);
425 ui_browser__gotorc(&self->b, row, 0);
426 slsmg_write_nstring(" ", offset + extra_offset);
427 slsmg_printf("%c ", folded_sign);
428 slsmg_write_nstring(str, width);
429 free(alloc_str);
430
431 if (++row == self->b.height)
432 goto out;
433do_next:
434 if (folded_sign == '+')
435 break;
436 }
437
438 if (folded_sign == '-') {
439 const int new_level = level + (extra_offset ? 2 : 1);
440 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
441 new_level, row, row_offset,
442 is_current_entry);
443 }
444 if (row == self->b.height)
445 goto out;
446 node = next;
447 }
448out:
449 return row - first_row;
450}
451
452static int hist_browser__show_callchain_node(struct hist_browser *self,
453 struct callchain_node *node,
454 int level, unsigned short row,
455 off_t *row_offset,
456 bool *is_current_entry)
457{
458 struct callchain_list *chain;
459 int first_row = row,
460 offset = level * LEVEL_OFFSET_STEP,
461 width = self->b.width - offset;
462 char folded_sign = ' ';
463
464 list_for_each_entry(chain, &node->val, list) {
465 char ipstr[BITS_PER_LONG / 4 + 1], *s;
466 int color;
467
468 folded_sign = callchain_list__folded(chain);
469
470 if (*row_offset != 0) {
471 --*row_offset;
472 continue;
473 }
474
475 color = HE_COLORSET_NORMAL;
476 if (ui_browser__is_current_entry(&self->b, row)) {
477 self->selection = &chain->ms;
478 color = HE_COLORSET_SELECTED;
479 *is_current_entry = true;
480 }
481
482 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
483 ui_browser__gotorc(&self->b, row, 0);
484 ui_browser__set_color(&self->b, color);
485 slsmg_write_nstring(" ", offset);
486 slsmg_printf("%c ", folded_sign);
487 slsmg_write_nstring(s, width - 2);
488
489 if (++row == self->b.height)
490 goto out;
491 }
492
493 if (folded_sign == '-')
494 row += hist_browser__show_callchain_node_rb_tree(self, node,
495 self->hists->stats.total_period,
496 level + 1, row,
497 row_offset,
498 is_current_entry);
499out:
500 return row - first_row;
501}
502
503static int hist_browser__show_callchain(struct hist_browser *self,
504 struct rb_root *chain,
505 int level, unsigned short row,
506 off_t *row_offset,
507 bool *is_current_entry)
508{
509 struct rb_node *nd;
510 int first_row = row;
511
512 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
513 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
514
515 row += hist_browser__show_callchain_node(self, node, level,
516 row, row_offset,
517 is_current_entry);
518 if (row == self->b.height)
519 break;
520 }
521
522 return row - first_row;
523}
524
525static int hist_browser__show_entry(struct hist_browser *self,
526 struct hist_entry *entry,
527 unsigned short row)
528{
529 char s[256];
530 double percent;
531 int printed = 0;
532 int color, width = self->b.width;
533 char folded_sign = ' ';
534 bool current_entry = ui_browser__is_current_entry(&self->b, row);
535 off_t row_offset = entry->row_offset;
536
537 if (current_entry) {
538 self->he_selection = entry;
539 self->selection = &entry->ms;
540 }
541
542 if (symbol_conf.use_callchain) {
543 hist_entry__init_have_children(entry);
544 folded_sign = hist_entry__folded(entry);
545 }
546
547 if (row_offset == 0) {
548 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
549 0, false, self->hists->stats.total_period);
550 percent = (entry->period * 100.0) / self->hists->stats.total_period;
551
552 color = HE_COLORSET_SELECTED;
553 if (!current_entry) {
554 if (percent >= MIN_RED)
555 color = HE_COLORSET_TOP;
556 else if (percent >= MIN_GREEN)
557 color = HE_COLORSET_MEDIUM;
558 else
559 color = HE_COLORSET_NORMAL;
560 }
561
562 ui_browser__set_color(&self->b, color);
563 ui_browser__gotorc(&self->b, row, 0);
564 if (symbol_conf.use_callchain) {
565 slsmg_printf("%c ", folded_sign);
566 width -= 2;
567 }
568 slsmg_write_nstring(s, width);
569 ++row;
570 ++printed;
571 } else
572 --row_offset;
573
574 if (folded_sign == '-' && row != self->b.height) {
575 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
576 1, row, &row_offset,
577 &current_entry);
578 if (current_entry)
579 self->he_selection = entry;
580 }
581
582 return printed;
583}
584
585static unsigned int hist_browser__refresh(struct ui_browser *self)
586{
587 unsigned row = 0;
588 struct rb_node *nd;
589 struct hist_browser *hb = container_of(self, struct hist_browser, b);
590
591 if (self->top == NULL)
592 self->top = rb_first(&hb->hists->entries);
593
594 for (nd = self->top; nd; nd = rb_next(nd)) {
595 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
596
597 if (h->filtered)
598 continue;
599
600 row += hist_browser__show_entry(hb, h, row);
601 if (row == self->height)
602 break;
603 }
604
605 return row;
606}
607
608static struct rb_node *hists__filter_entries(struct rb_node *nd)
609{
610 while (nd != NULL) {
611 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
612 if (!h->filtered)
613 return nd;
614
615 nd = rb_next(nd);
616 }
617
618 return NULL;
619}
620
621static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
622{
623 while (nd != NULL) {
624 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
625 if (!h->filtered)
626 return nd;
627
628 nd = rb_prev(nd);
629 }
630
631 return NULL;
632}
633
634static void ui_browser__hists_seek(struct ui_browser *self,
635 off_t offset, int whence)
636{
637 struct hist_entry *h;
638 struct rb_node *nd;
639 bool first = true;
640
641 switch (whence) {
642 case SEEK_SET:
643 nd = hists__filter_entries(rb_first(self->entries));
644 break;
645 case SEEK_CUR:
646 nd = self->top;
647 goto do_offset;
648 case SEEK_END:
649 nd = hists__filter_prev_entries(rb_last(self->entries));
650 first = false;
651 break;
652 default:
653 return;
654 }
655
656 /*
657 * Moves not relative to the first visible entry invalidates its
658 * row_offset:
659 */
660 h = rb_entry(self->top, struct hist_entry, rb_node);
661 h->row_offset = 0;
662
663 /*
664 * Here we have to check if nd is expanded (+), if it is we can't go
665 * the next top level hist_entry, instead we must compute an offset of
666 * what _not_ to show and not change the first visible entry.
667 *
668 * This offset increments when we are going from top to bottom and
669 * decreases when we're going from bottom to top.
670 *
671 * As we don't have backpointers to the top level in the callchains
672 * structure, we need to always print the whole hist_entry callchain,
673 * skipping the first ones that are before the first visible entry
674 * and stop when we printed enough lines to fill the screen.
675 */
676do_offset:
677 if (offset > 0) {
678 do {
679 h = rb_entry(nd, struct hist_entry, rb_node);
680 if (h->ms.unfolded) {
681 u16 remaining = h->nr_rows - h->row_offset;
682 if (offset > remaining) {
683 offset -= remaining;
684 h->row_offset = 0;
685 } else {
686 h->row_offset += offset;
687 offset = 0;
688 self->top = nd;
689 break;
690 }
691 }
692 nd = hists__filter_entries(rb_next(nd));
693 if (nd == NULL)
694 break;
695 --offset;
696 self->top = nd;
697 } while (offset != 0);
698 } else if (offset < 0) {
699 while (1) {
700 h = rb_entry(nd, struct hist_entry, rb_node);
701 if (h->ms.unfolded) {
702 if (first) {
703 if (-offset > h->row_offset) {
704 offset += h->row_offset;
705 h->row_offset = 0;
706 } else {
707 h->row_offset += offset;
708 offset = 0;
709 self->top = nd;
710 break;
711 }
712 } else {
713 if (-offset > h->nr_rows) {
714 offset += h->nr_rows;
715 h->row_offset = 0;
716 } else {
717 h->row_offset = h->nr_rows + offset;
718 offset = 0;
719 self->top = nd;
720 break;
721 }
722 }
723 }
724
725 nd = hists__filter_prev_entries(rb_prev(nd));
726 if (nd == NULL)
727 break;
728 ++offset;
729 self->top = nd;
730 if (offset == 0) {
731 /*
732 * Last unfiltered hist_entry, check if it is
733 * unfolded, if it is then we should have
734 * row_offset at its last entry.
735 */
736 h = rb_entry(nd, struct hist_entry, rb_node);
737 if (h->ms.unfolded)
738 h->row_offset = h->nr_rows;
739 break;
740 }
741 first = false;
742 }
743 } else {
744 self->top = nd;
745 h = rb_entry(nd, struct hist_entry, rb_node);
746 h->row_offset = 0;
747 }
748}
749
750static struct hist_browser *hist_browser__new(struct hists *hists)
751{
752 struct hist_browser *self = zalloc(sizeof(*self));
753
754 if (self) {
755 self->hists = hists;
756 self->b.refresh = hist_browser__refresh;
757 self->b.seek = ui_browser__hists_seek;
758 }
759
760 return self;
761}
762
763static void hist_browser__delete(struct hist_browser *self)
764{
765 free(self);
766}
767
768static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
769{
770 return self->he_selection;
771}
772
773static struct thread *hist_browser__selected_thread(struct hist_browser *self)
774{
775 return self->he_selection->thread;
776}
777
778static int hists__browser_title(struct hists *self, char *bf, size_t size,
779 const char *ev_name, const struct dso *dso,
780 const struct thread *thread)
781{
782 char unit;
783 int printed;
784 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
785
786 nr_events = convert_unit(nr_events, &unit);
787 printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
788
789 if (thread)
790 printed += snprintf(bf + printed, size - printed,
791 ", Thread: %s(%d)",
792 (thread->comm_set ? thread->comm : ""),
793 thread->pid);
794 if (dso)
795 printed += snprintf(bf + printed, size - printed,
796 ", DSO: %s", dso->short_name);
797 return printed;
798}
799
800int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
801{
802 struct hist_browser *browser = hist_browser__new(self);
803 struct pstack *fstack;
804 const struct thread *thread_filter = NULL;
805 const struct dso *dso_filter = NULL;
806 char msg[160];
807 int key = -1;
808
809 if (browser == NULL)
810 return -1;
811
812 fstack = pstack__new(2);
813 if (fstack == NULL)
814 goto out;
815
816 ui_helpline__push(helpline);
817
818 hists__browser_title(self, msg, sizeof(msg), ev_name,
819 dso_filter, thread_filter);
820 while (1) {
821 const struct thread *thread;
822 const struct dso *dso;
823 char *options[16];
824 int nr_options = 0, choice = 0, i,
825 annotate = -2, zoom_dso = -2, zoom_thread = -2,
826 browse_map = -2;
827
828 key = hist_browser__run(browser, msg);
829
830 thread = hist_browser__selected_thread(browser);
831 dso = browser->selection->map ? browser->selection->map->dso : NULL;
832
833 switch (key) {
834 case NEWT_KEY_TAB:
835 case NEWT_KEY_UNTAB:
836 /*
837 * Exit the browser, let hists__browser_tree
838 * go to the next or previous
839 */
840 goto out_free_stack;
841 case 'a':
842 if (browser->selection->map == NULL &&
843 browser->selection->map->dso->annotate_warned)
844 continue;
845 goto do_annotate;
846 case 'd':
847 goto zoom_dso;
848 case 't':
849 goto zoom_thread;
850 case NEWT_KEY_F1:
851 case 'h':
852 case '?':
853 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
854 "<- Zoom out\n"
855 "a Annotate current symbol\n"
856 "h/?/F1 Show this window\n"
857 "C Collapse all callchains\n"
858 "E Expand all callchains\n"
859 "d Zoom into current DSO\n"
860 "t Zoom into current Thread\n"
861 "q/CTRL+C Exit browser");
862 continue;
863 case NEWT_KEY_ENTER:
864 case NEWT_KEY_RIGHT:
865 /* menu */
866 break;
867 case NEWT_KEY_LEFT: {
868 const void *top;
869
870 if (pstack__empty(fstack))
871 continue;
872 top = pstack__pop(fstack);
873 if (top == &dso_filter)
874 goto zoom_out_dso;
875 if (top == &thread_filter)
876 goto zoom_out_thread;
877 continue;
878 }
879 case NEWT_KEY_ESCAPE:
880 if (!ui__dialog_yesno("Do you really want to exit?"))
881 continue;
882 /* Fall thru */
883 default:
884 goto out_free_stack;
885 }
886
887 if (browser->selection->sym != NULL &&
888 !browser->selection->map->dso->annotate_warned &&
889 asprintf(&options[nr_options], "Annotate %s",
890 browser->selection->sym->name) > 0)
891 annotate = nr_options++;
892
893 if (thread != NULL &&
894 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
895 (thread_filter ? "out of" : "into"),
896 (thread->comm_set ? thread->comm : ""),
897 thread->pid) > 0)
898 zoom_thread = nr_options++;
899
900 if (dso != NULL &&
901 asprintf(&options[nr_options], "Zoom %s %s DSO",
902 (dso_filter ? "out of" : "into"),
903 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
904 zoom_dso = nr_options++;
905
906 if (browser->selection->map != NULL &&
907 asprintf(&options[nr_options], "Browse map details") > 0)
908 browse_map = nr_options++;
909
910 options[nr_options++] = (char *)"Exit";
911
912 choice = ui__popup_menu(nr_options, options);
913
914 for (i = 0; i < nr_options - 1; ++i)
915 free(options[i]);
916
917 if (choice == nr_options - 1)
918 break;
919
920 if (choice == -1)
921 continue;
922
923 if (choice == annotate) {
924 struct hist_entry *he;
925do_annotate:
926 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
927 browser->selection->map->dso->annotate_warned = 1;
928 ui_helpline__puts("No vmlinux file found, can't "
929 "annotate with just a "
930 "kallsyms file");
931 continue;
932 }
933
934 he = hist_browser__selected_entry(browser);
935 if (he == NULL)
936 continue;
937
938 hist_entry__tui_annotate(he);
939 } else if (choice == browse_map)
940 map__browse(browser->selection->map);
941 else if (choice == zoom_dso) {
942zoom_dso:
943 if (dso_filter) {
944 pstack__remove(fstack, &dso_filter);
945zoom_out_dso:
946 ui_helpline__pop();
947 dso_filter = NULL;
948 } else {
949 if (dso == NULL)
950 continue;
951 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
952 dso->kernel ? "the Kernel" : dso->short_name);
953 dso_filter = dso;
954 pstack__push(fstack, &dso_filter);
955 }
956 hists__filter_by_dso(self, dso_filter);
957 hists__browser_title(self, msg, sizeof(msg), ev_name,
958 dso_filter, thread_filter);
959 hist_browser__reset(browser);
960 } else if (choice == zoom_thread) {
961zoom_thread:
962 if (thread_filter) {
963 pstack__remove(fstack, &thread_filter);
964zoom_out_thread:
965 ui_helpline__pop();
966 thread_filter = NULL;
967 } else {
968 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
969 thread->comm_set ? thread->comm : "",
970 thread->pid);
971 thread_filter = thread;
972 pstack__push(fstack, &thread_filter);
973 }
974 hists__filter_by_thread(self, thread_filter);
975 hists__browser_title(self, msg, sizeof(msg), ev_name,
976 dso_filter, thread_filter);
977 hist_browser__reset(browser);
978 }
979 }
980out_free_stack:
981 pstack__delete(fstack);
982out:
983 hist_browser__delete(browser);
984 return key;
985}
986
987int hists__tui_browse_tree(struct rb_root *self, const char *help)
988{
989 struct rb_node *first = rb_first(self), *nd = first, *next;
990 int key = 0;
991
992 while (nd) {
993 struct hists *hists = rb_entry(nd, struct hists, rb_node);
994 const char *ev_name = __event_name(hists->type, hists->config);
995
996 key = hists__browse(hists, help, ev_name);
997 switch (key) {
998 case NEWT_KEY_TAB:
999 next = rb_next(nd);
1000 if (next)
1001 nd = next;
1002 break;
1003 case NEWT_KEY_UNTAB:
1004 if (nd == first)
1005 continue;
1006 nd = rb_prev(nd);
1007 default:
1008 return key;
1009 }
1010 }
1011
1012 return key;
1013}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 000000000000..e35437dfa5b4
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,155 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <sys/ttydefaults.h>
4#include <ctype.h>
5#include <string.h>
6#include <linux/bitops.h>
7#include "../../debug.h"
8#include "../../symbol.h"
9#include "../browser.h"
10#include "../helpline.h"
11#include "map.h"
12
13static int ui_entry__read(const char *title, char *bf, size_t size, int width)
14{
15 struct newtExitStruct es;
16 newtComponent form, entry;
17 const char *result;
18 int err = -1;
19
20 newtCenteredWindow(width, 1, title);
21 form = newtForm(NULL, NULL, 0);
22 if (form == NULL)
23 return -1;
24
25 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
26 if (entry == NULL)
27 goto out_free_form;
28
29 newtFormAddComponent(form, entry);
30 newtFormAddHotKey(form, NEWT_KEY_ENTER);
31 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
32 newtFormAddHotKey(form, NEWT_KEY_LEFT);
33 newtFormAddHotKey(form, CTRL('c'));
34 newtFormRun(form, &es);
35
36 if (result != NULL) {
37 strncpy(bf, result, size);
38 err = 0;
39 }
40out_free_form:
41 newtPopWindow();
42 newtFormDestroy(form);
43 return 0;
44}
45
46struct map_browser {
47 struct ui_browser b;
48 struct map *map;
49 u8 addrlen;
50};
51
52static void map_browser__write(struct ui_browser *self, void *nd, int row)
53{
54 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
55 struct map_browser *mb = container_of(self, struct map_browser, b);
56 bool current_entry = ui_browser__is_current_entry(self, row);
57 int width;
58
59 ui_browser__set_percent_color(self, 0, current_entry);
60 slsmg_printf("%*llx %*llx %c ",
61 mb->addrlen, sym->start, mb->addrlen, sym->end,
62 sym->binding == STB_GLOBAL ? 'g' :
63 sym->binding == STB_LOCAL ? 'l' : 'w');
64 width = self->width - ((mb->addrlen * 2) + 4);
65 if (width > 0)
66 slsmg_write_nstring(sym->name, width);
67}
68
69/* FIXME uber-kludgy, see comment on cmd_report... */
70static u32 *symbol__browser_index(struct symbol *self)
71{
72 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
73}
74
75static int map_browser__search(struct map_browser *self)
76{
77 char target[512];
78 struct symbol *sym;
79 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
80
81 if (err)
82 return err;
83
84 if (target[0] == '0' && tolower(target[1]) == 'x') {
85 u64 addr = strtoull(target, NULL, 16);
86 sym = map__find_symbol(self->map, addr, NULL);
87 } else
88 sym = map__find_symbol_by_name(self->map, target, NULL);
89
90 if (sym != NULL) {
91 u32 *idx = symbol__browser_index(sym);
92
93 self->b.top = &sym->rb_node;
94 self->b.index = self->b.top_idx = *idx;
95 } else
96 ui_helpline__fpush("%s not found!", target);
97
98 return 0;
99}
100
101static int map_browser__run(struct map_browser *self)
102{
103 int key;
104
105 if (ui_browser__show(&self->b, self->map->dso->long_name,
106 "Press <- or ESC to exit, %s / to search",
107 verbose ? "" : "restart with -v to use") < 0)
108 return -1;
109
110 if (verbose)
111 ui_browser__add_exit_key(&self->b, '/');
112
113 while (1) {
114 key = ui_browser__run(&self->b);
115
116 if (verbose && key == '/')
117 map_browser__search(self);
118 else
119 break;
120 }
121
122 ui_browser__hide(&self->b);
123 return key;
124}
125
126int map__browse(struct map *self)
127{
128 struct map_browser mb = {
129 .b = {
130 .entries = &self->dso->symbols[self->type],
131 .refresh = ui_browser__rb_tree_refresh,
132 .seek = ui_browser__rb_tree_seek,
133 .write = map_browser__write,
134 },
135 .map = self,
136 };
137 struct rb_node *nd;
138 char tmp[BITS_PER_LONG / 4];
139 u64 maxaddr = 0;
140
141 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
142 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
143
144 if (maxaddr < pos->end)
145 maxaddr = pos->end;
146 if (verbose) {
147 u32 *idx = symbol__browser_index(pos);
148 *idx = mb.b.nr_entries;
149 }
150 ++mb.b.nr_entries;
151 }
152
153 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
154 return map_browser__run(&mb);
155}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 000000000000..8d79daa4458a
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8
9void ui_helpline__pop(void)
10{
11 newtPopHelpLine();
12}
13
14void ui_helpline__push(const char *msg)
15{
16 newtPushHelpLine(msg);
17}
18
19void ui_helpline__vpush(const char *fmt, va_list ap)
20{
21 char *s;
22
23 if (vasprintf(&s, fmt, ap) < 0)
24 vfprintf(stderr, fmt, ap);
25 else {
26 ui_helpline__push(s);
27 free(s);
28 }
29}
30
31void ui_helpline__fpush(const char *fmt, ...)
32{
33 va_list ap;
34
35 va_start(ap, fmt);
36 ui_helpline__vpush(fmt, ap);
37 va_end(ap);
38}
39
40void ui_helpline__puts(const char *msg)
41{
42 ui_helpline__pop();
43 ui_helpline__push(msg);
44}
45
46void ui_helpline__init(void)
47{
48 ui_helpline__puts(" ");
49}
50
51char ui_helpline__last_msg[1024];
52
53int ui_helpline__show_help(const char *format, va_list ap)
54{
55 int ret;
56 static int backlog;
57
58 ret = vsnprintf(ui_helpline__last_msg + backlog,
59 sizeof(ui_helpline__last_msg) - backlog, format, ap);
60 backlog += ret;
61
62 if (ui_helpline__last_msg[backlog - 1] == '\n') {
63 ui_helpline__puts(ui_helpline__last_msg);
64 newtRefresh();
65 backlog = 0;
66 }
67
68 return ret;
69}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 000000000000..ab6028d0c401
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 000000000000..5623da8e8080
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)msg, ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)msg, len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 000000000000..d7fc399d36b3
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 000000000000..a3820a0beb5b
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 000000000000..662085032eb7
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,42 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9
10static void newt_suspend(void *d __used)
11{
12 newtSuspend();
13 raise(SIGTSTP);
14 newtResume();
15}
16
17void setup_browser(void)
18{
19 if (!isatty(1) || !use_browser || dump_trace) {
20 use_browser = 0;
21 setup_pager();
22 return;
23 }
24
25 use_browser = 1;
26 newtInit();
27 newtCls();
28 newtSetSuspendCallback(newt_suspend, NULL);
29 ui_helpline__init();
30 ui_browser__init();
31}
32
33void exit_browser(bool wait_for_ok)
34{
35 if (use_browser > 0) {
36 if (wait_for_ok) {
37 char title[] = "Fatal Error", ok[] = "Ok";
38 newtWinMessage(title, ok, ui_helpline__last_msg);
39 }
40 newtFinished();
41 }
42}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 000000000000..056c69521a38
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,113 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "util.h"
13
14static void newt_form__set_exit_keys(newtComponent self)
15{
16 newtFormAddHotKey(self, NEWT_KEY_LEFT);
17 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
18 newtFormAddHotKey(self, 'Q');
19 newtFormAddHotKey(self, 'q');
20 newtFormAddHotKey(self, CTRL('c'));
21}
22
23static newtComponent newt_form__new(void)
24{
25 newtComponent self = newtForm(NULL, NULL, 0);
26 if (self)
27 newt_form__set_exit_keys(self);
28 return self;
29}
30
31int ui__popup_menu(int argc, char * const argv[])
32{
33 struct newtExitStruct es;
34 int i, rc = -1, max_len = 5;
35 newtComponent listbox, form = newt_form__new();
36
37 if (form == NULL)
38 return -1;
39
40 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
41 if (listbox == NULL)
42 goto out_destroy_form;
43
44 newtFormAddComponent(form, listbox);
45
46 for (i = 0; i < argc; ++i) {
47 int len = strlen(argv[i]);
48 if (len > max_len)
49 max_len = len;
50 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
51 goto out_destroy_form;
52 }
53
54 newtCenteredWindow(max_len, argc, NULL);
55 newtFormRun(form, &es);
56 rc = newtListboxGetCurrent(listbox) - NULL;
57 if (es.reason == NEWT_EXIT_HOTKEY)
58 rc = -1;
59 newtPopWindow();
60out_destroy_form:
61 newtFormDestroy(form);
62 return rc;
63}
64
65int ui__help_window(const char *text)
66{
67 struct newtExitStruct es;
68 newtComponent tb, form = newt_form__new();
69 int rc = -1;
70 int max_len = 0, nr_lines = 0;
71 const char *t;
72
73 if (form == NULL)
74 return -1;
75
76 t = text;
77 while (1) {
78 const char *sep = strchr(t, '\n');
79 int len;
80
81 if (sep == NULL)
82 sep = strchr(t, '\0');
83 len = sep - t;
84 if (max_len < len)
85 max_len = len;
86 ++nr_lines;
87 if (*sep == '\0')
88 break;
89 t = sep + 1;
90 }
91
92 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
93 if (tb == NULL)
94 goto out_destroy_form;
95
96 newtTextboxSetText(tb, text);
97 newtFormAddComponent(form, tb);
98 newtCenteredWindow(max_len, nr_lines, NULL);
99 newtFormRun(form, &es);
100 newtPopWindow();
101 rc = 0;
102out_destroy_form:
103 newtFormDestroy(form);
104 return rc;
105}
106
107static const char yes[] = "Yes", no[] = "No";
108
109bool ui__dialog_yesno(const char *msg)
110{
111 /* newtWinChoice should really be accepting const char pointers... */
112 return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
113}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 000000000000..afcbc1d99531
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index f380fed74359..7562707ddd1c 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -266,19 +266,6 @@ bool strglobmatch(const char *str, const char *pat);
266bool strlazymatch(const char *str, const char *pat); 266bool strlazymatch(const char *str, const char *pat);
267unsigned long convert_unit(unsigned long value, char *unit); 267unsigned long convert_unit(unsigned long value, char *unit);
268 268
269#ifndef ESC
270#define ESC 27
271#endif
272
273static inline bool is_exit_key(int key)
274{
275 char up;
276 if (key == CTRL('c') || key == ESC)
277 return true;
278 up = toupper(key);
279 return up == 'Q';
280}
281
282#define _STR(x) #x 269#define _STR(x) #x
283#define STR(x) _STR(x) 270#define STR(x) _STR(x)
284 271