aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-11-03 10:51:26 -0400
committerTakashi Iwai <tiwai@suse.de>2010-11-03 10:51:26 -0400
commit69dbdd819599e2f3b77c172e83af512845bca5ad (patch)
tree49939d8b80ec2115a801eae2aebc21f23867c876 /tools/perf/util
parent87232dd49aeb6b7d1af291edca8bd129a82ef4b5 (diff)
parent75e3f3137cb570661c2ad3035a139dda671fbb63 (diff)
Merge branch 'fix/asoc' into for-linus
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.h27
-rw-r--r--tools/perf/util/debug.c4
-rw-r--r--tools/perf/util/debug.h2
-rw-r--r--tools/perf/util/hist.c4
-rw-r--r--tools/perf/util/map.h10
-rw-r--r--tools/perf/util/path.c3
-rw-r--r--tools/perf/util/probe-event.c189
-rw-r--r--tools/perf/util/probe-event.h16
-rw-r--r--tools/perf/util/probe-finder.c645
-rw-r--r--tools/perf/util/probe-finder.h31
-rw-r--r--tools/perf/util/sort.h2
-rw-r--r--tools/perf/util/symbol.c14
-rw-r--r--tools/perf/util/symbol.h1
-rw-r--r--tools/perf/util/ui/browser.c118
-rw-r--r--tools/perf/util/ui/browser.h9
-rw-r--r--tools/perf/util/ui/browsers/annotate.c38
-rw-r--r--tools/perf/util/ui/browsers/hists.c327
-rw-r--r--tools/perf/util/ui/browsers/map.c32
-rw-r--r--tools/perf/util/ui/util.c4
-rw-r--r--tools/perf/util/util.h13
22 files changed, 1128 insertions, 461 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 6de4313924fb..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,15 +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->children_hit = 0; 58 root->node.parent = NULL;
54 node->parent = NULL; 59 root->node.hit = 0;
55 node->hit = 0; 60 root->node.children_hit = 0;
61 root->max_depth = 0;
56} 62}
57 63
58static inline u64 cumul_hits(struct callchain_node *node) 64static inline u64 cumul_hits(struct callchain_node *node)
@@ -61,8 +67,9 @@ static inline u64 cumul_hits(struct callchain_node *node)
61} 67}
62 68
63int register_callchain_param(struct callchain_param *param); 69int register_callchain_param(struct callchain_param *param);
64int append_chain(struct callchain_node *root, struct ip_callchain *chain, 70int callchain_append(struct callchain_root *root, struct ip_callchain *chain,
65 struct map_symbol *syms, u64 period); 71 struct map_symbol *syms, u64 period);
72int callchain_merge(struct callchain_root *dst, struct callchain_root *src);
66 73
67bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); 74bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
68#endif /* __PERF_CALLCHAIN_H */ 75#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index f9c7e3ad1aa7..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{
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 7a17ee061bcb..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);
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index be22ae6ef055..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 }
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/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 fcc16e4349df..3b6a5297bf16 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,61 @@ 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
118 if (module) {
119 list_for_each_entry(dso, &machine.kernel_dsos, node) {
120 if (strncmp(dso->short_name + 1, module,
121 dso->short_name_len - 2) == 0)
122 goto found;
123 }
124 pr_debug("Failed to find module %s.\n", module);
125 return NULL;
126 } else {
127 dso = machine.vmlinux_maps[MAP__FUNCTION]->dso;
128 if (dso__load_vmlinux_path(dso,
129 machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
130 pr_debug("Failed to load kernel map.\n");
131 return NULL;
132 }
133 }
134found:
135 return dso->long_name;
136}
137
112#ifdef DWARF_SUPPORT 138#ifdef DWARF_SUPPORT
113static int open_vmlinux(void) 139static int open_vmlinux(const char *module)
114{ 140{
115 if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { 141 const char *path = kernel_get_module_path(module);
116 pr_debug("Failed to load kernel map.\n"); 142 if (!path) {
117 return -EINVAL; 143 pr_err("Failed to find path of %s module", module ?: "kernel");
144 return -ENOENT;
118 } 145 }
119 pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); 146 pr_debug("Try to open %s\n", path);
120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); 147 return open(path, O_RDONLY);
121} 148}
122 149
123/* 150/*
@@ -125,20 +152,19 @@ static int open_vmlinux(void)
125 * Currently only handles kprobes. 152 * Currently only handles kprobes.
126 */ 153 */
127static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 154static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
128 struct perf_probe_point *pp) 155 struct perf_probe_point *pp)
129{ 156{
130 struct symbol *sym; 157 struct symbol *sym;
131 int fd, ret = -ENOENT; 158 struct map *map;
159 u64 addr;
160 int ret = -ENOENT;
132 161
133 sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], 162 sym = __find_kernel_function_by_name(tp->symbol, &map);
134 tp->symbol, NULL);
135 if (sym) { 163 if (sym) {
136 fd = open_vmlinux(); 164 addr = map->unmap_ip(map, sym->start + tp->offset);
137 if (fd >= 0) { 165 pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
138 ret = find_perf_probe_point(fd, 166 tp->offset, addr);
139 sym->start + tp->offset, pp); 167 ret = find_perf_probe_point((unsigned long)addr, pp);
140 close(fd);
141 }
142 } 168 }
143 if (ret <= 0) { 169 if (ret <= 0) {
144 pr_debug("Failed to find corresponding probes from " 170 pr_debug("Failed to find corresponding probes from "
@@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
156/* Try to find perf_probe_event with debuginfo */ 182/* Try to find perf_probe_event with debuginfo */
157static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 183static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
158 struct probe_trace_event **tevs, 184 struct probe_trace_event **tevs,
159 int max_tevs) 185 int max_tevs, const char *module)
160{ 186{
161 bool need_dwarf = perf_probe_event_need_dwarf(pev); 187 bool need_dwarf = perf_probe_event_need_dwarf(pev);
162 int fd, ntevs; 188 int fd, ntevs;
163 189
164 fd = open_vmlinux(); 190 fd = open_vmlinux(module);
165 if (fd < 0) { 191 if (fd < 0) {
166 if (need_dwarf) { 192 if (need_dwarf) {
167 pr_warning("Failed to open debuginfo file.\n"); 193 pr_warning("Failed to open debuginfo file.\n");
@@ -300,7 +326,7 @@ error:
300 * Show line-range always requires debuginfo to find source file and 326 * Show line-range always requires debuginfo to find source file and
301 * line number. 327 * line number.
302 */ 328 */
303int show_line_range(struct line_range *lr) 329int show_line_range(struct line_range *lr, const char *module)
304{ 330{
305 int l = 1; 331 int l = 1;
306 struct line_node *ln; 332 struct line_node *ln;
@@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr)
313 if (ret < 0) 339 if (ret < 0)
314 return ret; 340 return ret;
315 341
316 fd = open_vmlinux(); 342 fd = open_vmlinux(module);
317 if (fd < 0) { 343 if (fd < 0) {
318 pr_warning("Failed to open debuginfo file.\n"); 344 pr_warning("Failed to open debuginfo file.\n");
319 return fd; 345 return fd;
@@ -378,11 +404,84 @@ end:
378 return ret; 404 return ret;
379} 405}
380 406
407static int show_available_vars_at(int fd, struct perf_probe_event *pev,
408 int max_vls, bool externs)
409{
410 char *buf;
411 int ret, i;
412 struct str_node *node;
413 struct variable_list *vls = NULL, *vl;
414
415 buf = synthesize_perf_probe_point(&pev->point);
416 if (!buf)
417 return -EINVAL;
418 pr_debug("Searching variables at %s\n", buf);
419
420 ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
421 if (ret > 0) {
422 /* Some variables were found */
423 fprintf(stdout, "Available variables at %s\n", buf);
424 for (i = 0; i < ret; i++) {
425 vl = &vls[i];
426 /*
427 * A probe point might be converted to
428 * several trace points.
429 */
430 fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
431 vl->point.offset);
432 free(vl->point.symbol);
433 if (vl->vars) {
434 strlist__for_each(node, vl->vars)
435 fprintf(stdout, "\t\t%s\n", node->s);
436 strlist__delete(vl->vars);
437 } else
438 fprintf(stdout, "(No variables)\n");
439 }
440 free(vls);
441 } else
442 pr_err("Failed to find variables at %s (%d)\n", buf, ret);
443
444 free(buf);
445 return ret;
446}
447
448/* Show available variables on given probe point */
449int show_available_vars(struct perf_probe_event *pevs, int npevs,
450 int max_vls, const char *module, bool externs)
451{
452 int i, fd, ret = 0;
453
454 ret = init_vmlinux();
455 if (ret < 0)
456 return ret;
457
458 fd = open_vmlinux(module);
459 if (fd < 0) {
460 pr_warning("Failed to open debuginfo file.\n");
461 return fd;
462 }
463
464 setup_pager();
465
466 for (i = 0; i < npevs && ret >= 0; i++)
467 ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
468
469 close(fd);
470 return ret;
471}
472
381#else /* !DWARF_SUPPORT */ 473#else /* !DWARF_SUPPORT */
382 474
383static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, 475static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
384 struct perf_probe_point *pp) 476 struct perf_probe_point *pp)
385{ 477{
478 struct symbol *sym;
479
480 sym = __find_kernel_function_by_name(tp->symbol, NULL);
481 if (!sym) {
482 pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
483 return -ENOENT;
484 }
386 pp->function = strdup(tp->symbol); 485 pp->function = strdup(tp->symbol);
387 if (pp->function == NULL) 486 if (pp->function == NULL)
388 return -ENOMEM; 487 return -ENOMEM;
@@ -394,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
394 493
395static int try_to_find_probe_trace_events(struct perf_probe_event *pev, 494static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
396 struct probe_trace_event **tevs __unused, 495 struct probe_trace_event **tevs __unused,
397 int max_tevs __unused) 496 int max_tevs __unused, const char *mod __unused)
398{ 497{
399 if (perf_probe_event_need_dwarf(pev)) { 498 if (perf_probe_event_need_dwarf(pev)) {
400 pr_warning("Debuginfo-analysis is not supported.\n"); 499 pr_warning("Debuginfo-analysis is not supported.\n");
@@ -403,12 +502,19 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
403 return 0; 502 return 0;
404} 503}
405 504
406int show_line_range(struct line_range *lr __unused) 505int show_line_range(struct line_range *lr __unused, const char *module __unused)
407{ 506{
408 pr_warning("Debuginfo-analysis is not supported.\n"); 507 pr_warning("Debuginfo-analysis is not supported.\n");
409 return -ENOSYS; 508 return -ENOSYS;
410} 509}
411 510
511int show_available_vars(struct perf_probe_event *pevs __unused,
512 int npevs __unused, int max_vls __unused,
513 const char *module __unused, bool externs __unused)
514{
515 pr_warning("Debuginfo-analysis is not supported.\n");
516 return -ENOSYS;
517}
412#endif 518#endif
413 519
414int parse_line_range_desc(const char *arg, struct line_range *lr) 520int parse_line_range_desc(const char *arg, struct line_range *lr)
@@ -1087,7 +1193,7 @@ error:
1087} 1193}
1088 1194
1089static int convert_to_perf_probe_event(struct probe_trace_event *tev, 1195static int convert_to_perf_probe_event(struct probe_trace_event *tev,
1090 struct perf_probe_event *pev) 1196 struct perf_probe_event *pev)
1091{ 1197{
1092 char buf[64] = ""; 1198 char buf[64] = "";
1093 int i, ret; 1199 int i, ret;
@@ -1516,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
1516 1622
1517static int convert_to_probe_trace_events(struct perf_probe_event *pev, 1623static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1518 struct probe_trace_event **tevs, 1624 struct probe_trace_event **tevs,
1519 int max_tevs) 1625 int max_tevs, const char *module)
1520{ 1626{
1521 struct symbol *sym; 1627 struct symbol *sym;
1522 int ret = 0, i; 1628 int ret = 0, i;
1523 struct probe_trace_event *tev; 1629 struct probe_trace_event *tev;
1524 1630
1525 /* Convert perf_probe_event with debuginfo */ 1631 /* Convert perf_probe_event with debuginfo */
1526 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); 1632 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
1527 if (ret != 0) 1633 if (ret != 0)
1528 return ret; 1634 return ret;
1529 1635
@@ -1572,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1572 } 1678 }
1573 1679
1574 /* Currently just checking function name from symbol map */ 1680 /* Currently just checking function name from symbol map */
1575 sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], 1681 sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
1576 tev->point.symbol, NULL);
1577 if (!sym) { 1682 if (!sym) {
1578 pr_warning("Kernel symbol \'%s\' not found.\n", 1683 pr_warning("Kernel symbol \'%s\' not found.\n",
1579 tev->point.symbol); 1684 tev->point.symbol);
@@ -1596,7 +1701,7 @@ struct __event_package {
1596}; 1701};
1597 1702
1598int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, 1703int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1599 bool force_add, int max_tevs) 1704 int max_tevs, const char *module, bool force_add)
1600{ 1705{
1601 int i, j, ret; 1706 int i, j, ret;
1602 struct __event_package *pkgs; 1707 struct __event_package *pkgs;
@@ -1617,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1617 pkgs[i].pev = &pevs[i]; 1722 pkgs[i].pev = &pevs[i];
1618 /* Convert with or without debuginfo */ 1723 /* Convert with or without debuginfo */
1619 ret = convert_to_probe_trace_events(pkgs[i].pev, 1724 ret = convert_to_probe_trace_events(pkgs[i].pev,
1620 &pkgs[i].tevs, max_tevs); 1725 &pkgs[i].tevs,
1726 max_tevs,
1727 module);
1621 if (ret < 0) 1728 if (ret < 0)
1622 goto end; 1729 goto end;
1623 pkgs[i].ntevs = ret; 1730 pkgs[i].ntevs = ret;
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 32b81f707ff5..3991d73d1cff 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)
116 } 116 }
117} 117}
118 118
119/* Dwarf FL wrappers */
120
121static int __linux_kernel_find_elf(Dwfl_Module *mod,
122 void **userdata,
123 const char *module_name,
124 Dwarf_Addr base,
125 char **file_name, Elf **elfp)
126{
127 int fd;
128 const char *path = kernel_get_module_path(module_name);
129
130 if (path) {
131 fd = open(path, O_RDONLY);
132 if (fd >= 0) {
133 *file_name = strdup(path);
134 return fd;
135 }
136 }
137 /* If failed, try to call standard method */
138 return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
139 file_name, elfp);
140}
141
142static char *debuginfo_path; /* Currently dummy */
143
144static const Dwfl_Callbacks offline_callbacks = {
145 .find_debuginfo = dwfl_standard_find_debuginfo,
146 .debuginfo_path = &debuginfo_path,
147
148 .section_address = dwfl_offline_section_address,
149
150 /* We use this table for core files too. */
151 .find_elf = dwfl_build_id_find_elf,
152};
153
154static const Dwfl_Callbacks kernel_callbacks = {
155 .find_debuginfo = dwfl_standard_find_debuginfo,
156 .debuginfo_path = &debuginfo_path,
157
158 .find_elf = __linux_kernel_find_elf,
159 .section_address = dwfl_linux_kernel_module_section_address,
160};
161
162/* Get a Dwarf from offline image */
163static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
164{
165 Dwfl_Module *mod;
166 Dwarf *dbg = NULL;
167
168 if (!dwflp)
169 return NULL;
170
171 *dwflp = dwfl_begin(&offline_callbacks);
172 if (!*dwflp)
173 return NULL;
174
175 mod = dwfl_report_offline(*dwflp, "", "", fd);
176 if (!mod)
177 goto error;
178
179 dbg = dwfl_module_getdwarf(mod, bias);
180 if (!dbg) {
181error:
182 dwfl_end(*dwflp);
183 *dwflp = NULL;
184 }
185 return dbg;
186}
187
188/* Get a Dwarf from live kernel image */
189static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
190 Dwarf_Addr *bias)
191{
192 Dwarf *dbg;
193
194 if (!dwflp)
195 return NULL;
196
197 *dwflp = dwfl_begin(&kernel_callbacks);
198 if (!*dwflp)
199 return NULL;
200
201 /* Load the kernel dwarves: Don't care the result here */
202 dwfl_linux_kernel_report_kernel(*dwflp);
203 dwfl_linux_kernel_report_modules(*dwflp);
204
205 dbg = dwfl_addrdwarf(*dwflp, addr, bias);
206 /* Here, check whether we could get a real dwarf */
207 if (!dbg) {
208 dwfl_end(*dwflp);
209 *dwflp = NULL;
210 }
211 return dbg;
212}
213
119/* Dwarf wrappers */ 214/* Dwarf wrappers */
120 215
121/* Find the realpath of the target file. */ 216/* Find the realpath of the target file. */
@@ -160,26 +255,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
160 return name ? (strcmp(tname, name) == 0) : false; 255 return name ? (strcmp(tname, name) == 0) : false;
161} 256}
162 257
163/* Get type die, but skip qualifiers and typedef */ 258/* Get type die */
164static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 259static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
165{ 260{
166 Dwarf_Attribute attr; 261 Dwarf_Attribute attr;
262
263 if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
264 dwarf_formref_die(&attr, die_mem))
265 return die_mem;
266 else
267 return NULL;
268}
269
270/* Get a type die, but skip qualifiers */
271static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
272{
167 int tag; 273 int tag;
168 274
169 do { 275 do {
170 if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || 276 vr_die = die_get_type(vr_die, die_mem);
171 dwarf_formref_die(&attr, die_mem) == NULL) 277 if (!vr_die)
172 return NULL; 278 break;
173 279 tag = dwarf_tag(vr_die);
174 tag = dwarf_tag(die_mem);
175 vr_die = die_mem;
176 } while (tag == DW_TAG_const_type || 280 } while (tag == DW_TAG_const_type ||
177 tag == DW_TAG_restrict_type || 281 tag == DW_TAG_restrict_type ||
178 tag == DW_TAG_volatile_type || 282 tag == DW_TAG_volatile_type ||
179 tag == DW_TAG_shared_type || 283 tag == DW_TAG_shared_type);
180 tag == DW_TAG_typedef); 284
285 return vr_die;
286}
181 287
182 return die_mem; 288/* Get a type die, but skip qualifiers and typedef */
289static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
290{
291 do {
292 vr_die = __die_get_real_type(vr_die, die_mem);
293 } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
294
295 return vr_die;
183} 296}
184 297
185static bool die_is_signed_type(Dwarf_Die *tp_die) 298static bool die_is_signed_type(Dwarf_Die *tp_die)
@@ -320,25 +433,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
320 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); 433 return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
321} 434}
322 435
436struct __find_variable_param {
437 const char *name;
438 Dwarf_Addr addr;
439};
440
323static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) 441static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
324{ 442{
325 const char *name = data; 443 struct __find_variable_param *fvp = data;
326 int tag; 444 int tag;
327 445
328 tag = dwarf_tag(die_mem); 446 tag = dwarf_tag(die_mem);
329 if ((tag == DW_TAG_formal_parameter || 447 if ((tag == DW_TAG_formal_parameter ||
330 tag == DW_TAG_variable) && 448 tag == DW_TAG_variable) &&
331 die_compare_name(die_mem, name)) 449 die_compare_name(die_mem, fvp->name))
332 return DIE_FIND_CB_FOUND; 450 return DIE_FIND_CB_FOUND;
333 451
334 return DIE_FIND_CB_CONTINUE; 452 if (dwarf_haspc(die_mem, fvp->addr))
453 return DIE_FIND_CB_CONTINUE;
454 else
455 return DIE_FIND_CB_SIBLING;
335} 456}
336 457
337/* Find a variable called 'name' */ 458/* Find a variable called 'name' at given address */
338static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, 459static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
339 Dwarf_Die *die_mem) 460 Dwarf_Addr addr, Dwarf_Die *die_mem)
340{ 461{
341 return die_find_child(sp_die, __die_find_variable_cb, (void *)name, 462 struct __find_variable_param fvp = { .name = name, .addr = addr};
463
464 return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
342 die_mem); 465 die_mem);
343} 466}
344 467
@@ -361,6 +484,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
361 die_mem); 484 die_mem);
362} 485}
363 486
487/* Get the name of given variable DIE */
488static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
489{
490 Dwarf_Die type;
491 int tag, ret, ret2;
492 const char *tmp = "";
493
494 if (__die_get_real_type(vr_die, &type) == NULL)
495 return -ENOENT;
496
497 tag = dwarf_tag(&type);
498 if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
499 tmp = "*";
500 else if (tag == DW_TAG_subroutine_type) {
501 /* Function pointer */
502 ret = snprintf(buf, len, "(function_type)");
503 return (ret >= len) ? -E2BIG : ret;
504 } else {
505 if (!dwarf_diename(&type))
506 return -ENOENT;
507 if (tag == DW_TAG_union_type)
508 tmp = "union ";
509 else if (tag == DW_TAG_structure_type)
510 tmp = "struct ";
511 /* Write a base name */
512 ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
513 return (ret >= len) ? -E2BIG : ret;
514 }
515 ret = die_get_typename(&type, buf, len);
516 if (ret > 0) {
517 ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
518 ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
519 }
520 return ret;
521}
522
523/* Get the name and type of given variable DIE, stored as "type\tname" */
524static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
525{
526 int ret, ret2;
527
528 ret = die_get_typename(vr_die, buf, len);
529 if (ret < 0) {
530 pr_debug("Failed to get type, make it unknown.\n");
531 ret = snprintf(buf, len, "(unknown_type)");
532 }
533 if (ret > 0) {
534 ret2 = snprintf(buf + ret, len - ret, "\t%s",
535 dwarf_diename(vr_die));
536 ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
537 }
538 return ret;
539}
540
364/* 541/*
365 * Probe finder related functions 542 * Probe finder related functions
366 */ 543 */
@@ -374,8 +551,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
374 return ref; 551 return ref;
375} 552}
376 553
377/* Show a location */ 554/*
378static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) 555 * Convert a location into trace_arg.
556 * If tvar == NULL, this just checks variable can be converted.
557 */
558static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
559 Dwarf_Op *fb_ops,
560 struct probe_trace_arg *tvar)
379{ 561{
380 Dwarf_Attribute attr; 562 Dwarf_Attribute attr;
381 Dwarf_Op *op; 563 Dwarf_Op *op;
@@ -384,20 +566,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
384 Dwarf_Word offs = 0; 566 Dwarf_Word offs = 0;
385 bool ref = false; 567 bool ref = false;
386 const char *regs; 568 const char *regs;
387 struct probe_trace_arg *tvar = pf->tvar;
388 int ret; 569 int ret;
389 570
571 if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
572 goto static_var;
573
390 /* TODO: handle more than 1 exprs */ 574 /* TODO: handle more than 1 exprs */
391 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || 575 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
392 dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || 576 dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
393 nops == 0) { 577 nops == 0) {
394 /* TODO: Support const_value */ 578 /* TODO: Support const_value */
395 pr_err("Failed to find the location of %s at this address.\n"
396 " Perhaps, it has been optimized out.\n", pf->pvar->var);
397 return -ENOENT; 579 return -ENOENT;
398 } 580 }
399 581
400 if (op->atom == DW_OP_addr) { 582 if (op->atom == DW_OP_addr) {
583static_var:
584 if (!tvar)
585 return 0;
401 /* Static variables on memory (not stack), make @varname */ 586 /* Static variables on memory (not stack), make @varname */
402 ret = strlen(dwarf_diename(vr_die)); 587 ret = strlen(dwarf_diename(vr_die));
403 tvar->value = zalloc(ret + 2); 588 tvar->value = zalloc(ret + 2);
@@ -412,14 +597,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
412 597
413 /* If this is based on frame buffer, set the offset */ 598 /* If this is based on frame buffer, set the offset */
414 if (op->atom == DW_OP_fbreg) { 599 if (op->atom == DW_OP_fbreg) {
415 if (pf->fb_ops == NULL) { 600 if (fb_ops == NULL)
416 pr_warning("The attribute of frame base is not "
417 "supported.\n");
418 return -ENOTSUP; 601 return -ENOTSUP;
419 }
420 ref = true; 602 ref = true;
421 offs = op->number; 603 offs = op->number;
422 op = &pf->fb_ops[0]; 604 op = &fb_ops[0];
423 } 605 }
424 606
425 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { 607 if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
@@ -435,13 +617,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
435 } else if (op->atom == DW_OP_regx) { 617 } else if (op->atom == DW_OP_regx) {
436 regn = op->number; 618 regn = op->number;
437 } else { 619 } else {
438 pr_warning("DW_OP %x is not supported.\n", op->atom); 620 pr_debug("DW_OP %x is not supported.\n", op->atom);
439 return -ENOTSUP; 621 return -ENOTSUP;
440 } 622 }
441 623
624 if (!tvar)
625 return 0;
626
442 regs = get_arch_regstr(regn); 627 regs = get_arch_regstr(regn);
443 if (!regs) { 628 if (!regs) {
444 pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); 629 /* This should be a bug in DWARF or this tool */
630 pr_warning("Mapping for DWARF register number %u "
631 "missing on this architecture.", regn);
445 return -ERANGE; 632 return -ERANGE;
446 } 633 }
447 634
@@ -666,8 +853,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
666 pr_debug("Converting variable %s into trace event.\n", 853 pr_debug("Converting variable %s into trace event.\n",
667 dwarf_diename(vr_die)); 854 dwarf_diename(vr_die));
668 855
669 ret = convert_variable_location(vr_die, pf); 856 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
670 if (ret == 0 && pf->pvar->field) { 857 pf->tvar);
858 if (ret == -ENOENT)
859 pr_err("Failed to find the location of %s at this address.\n"
860 " Perhaps, it has been optimized out.\n", pf->pvar->var);
861 else if (ret == -ENOTSUP)
862 pr_err("Sorry, we don't support this variable location yet.\n");
863 else if (pf->pvar->field) {
671 ret = convert_variable_fields(vr_die, pf->pvar->var, 864 ret = convert_variable_fields(vr_die, pf->pvar->var,
672 pf->pvar->field, &pf->tvar->ref, 865 pf->pvar->field, &pf->tvar->ref,
673 &die_mem); 866 &die_mem);
@@ -722,56 +915,39 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
722 pr_debug("Searching '%s' variable in context.\n", 915 pr_debug("Searching '%s' variable in context.\n",
723 pf->pvar->var); 916 pf->pvar->var);
724 /* Search child die for local variables and parameters. */ 917 /* Search child die for local variables and parameters. */
725 if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) 918 if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
726 ret = convert_variable(&vr_die, pf); 919 ret = convert_variable(&vr_die, pf);
727 else { 920 else {
728 /* Search upper class */ 921 /* Search upper class */
729 nscopes = dwarf_getscopes_die(sp_die, &scopes); 922 nscopes = dwarf_getscopes_die(sp_die, &scopes);
730 if (nscopes > 0) { 923 while (nscopes-- > 1) {
731 ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, 924 pr_debug("Searching variables in %s\n",
732 0, NULL, 0, 0, &vr_die); 925 dwarf_diename(&scopes[nscopes]));
733 if (ret >= 0) 926 /* We should check this scope, so give dummy address */
927 if (die_find_variable_at(&scopes[nscopes],
928 pf->pvar->var, 0,
929 &vr_die)) {
734 ret = convert_variable(&vr_die, pf); 930 ret = convert_variable(&vr_die, pf);
735 else 931 goto found;
736 ret = -ENOENT; 932 }
933 }
934 if (scopes)
737 free(scopes); 935 free(scopes);
738 } else 936 ret = -ENOENT;
739 ret = -ENOENT;
740 } 937 }
938found:
741 if (ret < 0) 939 if (ret < 0)
742 pr_warning("Failed to find '%s' in this function.\n", 940 pr_warning("Failed to find '%s' in this function.\n",
743 pf->pvar->var); 941 pf->pvar->var);
744 return ret; 942 return ret;
745} 943}
746 944
747/* Show a probe point to output buffer */ 945/* Convert subprogram DIE to trace point */
748static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) 946static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
947 bool retprobe, struct probe_trace_point *tp)
749{ 948{
750 struct probe_trace_event *tev;
751 Dwarf_Addr eaddr; 949 Dwarf_Addr eaddr;
752 Dwarf_Die die_mem;
753 const char *name; 950 const char *name;
754 int ret, i;
755 Dwarf_Attribute fb_attr;
756 size_t nops;
757
758 if (pf->ntevs == pf->max_tevs) {
759 pr_warning("Too many( > %d) probe point found.\n",
760 pf->max_tevs);
761 return -ERANGE;
762 }
763 tev = &pf->tevs[pf->ntevs++];
764
765 /* If no real subprogram, find a real one */
766 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
767 sp_die = die_find_real_subprogram(&pf->cu_die,
768 pf->addr, &die_mem);
769 if (!sp_die) {
770 pr_warning("Failed to find probe point in any "
771 "functions.\n");
772 return -ENOENT;
773 }
774 }
775 951
776 /* Copy the name of probe point */ 952 /* Copy the name of probe point */
777 name = dwarf_diename(sp_die); 953 name = dwarf_diename(sp_die);
@@ -781,26 +957,45 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
781 dwarf_diename(sp_die)); 957 dwarf_diename(sp_die));
782 return -ENOENT; 958 return -ENOENT;
783 } 959 }
784 tev->point.symbol = strdup(name); 960 tp->symbol = strdup(name);
785 if (tev->point.symbol == NULL) 961 if (tp->symbol == NULL)
786 return -ENOMEM; 962 return -ENOMEM;
787 tev->point.offset = (unsigned long)(pf->addr - eaddr); 963 tp->offset = (unsigned long)(paddr - eaddr);
788 } else 964 } else
789 /* This function has no name. */ 965 /* This function has no name. */
790 tev->point.offset = (unsigned long)pf->addr; 966 tp->offset = (unsigned long)paddr;
791 967
792 /* Return probe must be on the head of a subprogram */ 968 /* Return probe must be on the head of a subprogram */
793 if (pf->pev->point.retprobe) { 969 if (retprobe) {
794 if (tev->point.offset != 0) { 970 if (eaddr != paddr) {
795 pr_warning("Return probe must be on the head of" 971 pr_warning("Return probe must be on the head of"
796 " a real function\n"); 972 " a real function\n");
797 return -EINVAL; 973 return -EINVAL;
798 } 974 }
799 tev->point.retprobe = true; 975 tp->retprobe = true;
800 } 976 }
801 977
802 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, 978 return 0;
803 tev->point.offset); 979}
980
981/* Call probe_finder callback with real subprogram DIE */
982static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
983{
984 Dwarf_Die die_mem;
985 Dwarf_Attribute fb_attr;
986 size_t nops;
987 int ret;
988
989 /* If no real subprogram, find a real one */
990 if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
991 sp_die = die_find_real_subprogram(&pf->cu_die,
992 pf->addr, &die_mem);
993 if (!sp_die) {
994 pr_warning("Failed to find probe point in any "
995 "functions.\n");
996 return -ENOENT;
997 }
998 }
804 999
805 /* Get the frame base attribute/ops */ 1000 /* Get the frame base attribute/ops */
806 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); 1001 dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
@@ -820,22 +1015,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
820#endif 1015#endif
821 } 1016 }
822 1017
823 /* Find each argument */ 1018 /* Call finder's callback handler */
824 tev->nargs = pf->pev->nargs; 1019 ret = pf->callback(sp_die, pf);
825 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
826 if (tev->args == NULL)
827 return -ENOMEM;
828 for (i = 0; i < pf->pev->nargs; i++) {
829 pf->pvar = &pf->pev->args[i];
830 pf->tvar = &tev->args[i];
831 ret = find_variable(sp_die, pf);
832 if (ret != 0)
833 return ret;
834 }
835 1020
836 /* *pf->fb_ops will be cached in libdw. Don't free it. */ 1021 /* *pf->fb_ops will be cached in libdw. Don't free it. */
837 pf->fb_ops = NULL; 1022 pf->fb_ops = NULL;
838 return 0; 1023
1024 return ret;
839} 1025}
840 1026
841/* Find probe point from its line number */ 1027/* Find probe point from its line number */
@@ -871,7 +1057,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
871 (int)i, lineno, (uintmax_t)addr); 1057 (int)i, lineno, (uintmax_t)addr);
872 pf->addr = addr; 1058 pf->addr = addr;
873 1059
874 ret = convert_probe_point(NULL, pf); 1060 ret = call_probe_finder(NULL, pf);
875 /* Continuing, because target line might be inlined. */ 1061 /* Continuing, because target line might be inlined. */
876 } 1062 }
877 return ret; 1063 return ret;
@@ -984,7 +1170,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
984 (int)i, lineno, (unsigned long long)addr); 1170 (int)i, lineno, (unsigned long long)addr);
985 pf->addr = addr; 1171 pf->addr = addr;
986 1172
987 ret = convert_probe_point(sp_die, pf); 1173 ret = call_probe_finder(sp_die, pf);
988 /* Continuing, because target line might be inlined. */ 1174 /* Continuing, because target line might be inlined. */
989 } 1175 }
990 /* TODO: deallocate lines, but how? */ 1176 /* TODO: deallocate lines, but how? */
@@ -1019,7 +1205,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
1019 pr_debug("found inline addr: 0x%jx\n", 1205 pr_debug("found inline addr: 0x%jx\n",
1020 (uintmax_t)pf->addr); 1206 (uintmax_t)pf->addr);
1021 1207
1022 param->retval = convert_probe_point(in_die, pf); 1208 param->retval = call_probe_finder(in_die, pf);
1023 if (param->retval < 0) 1209 if (param->retval < 0)
1024 return DWARF_CB_ABORT; 1210 return DWARF_CB_ABORT;
1025 } 1211 }
@@ -1057,7 +1243,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
1057 } 1243 }
1058 pf->addr += pp->offset; 1244 pf->addr += pp->offset;
1059 /* TODO: Check the address in this function */ 1245 /* TODO: Check the address in this function */
1060 param->retval = convert_probe_point(sp_die, pf); 1246 param->retval = call_probe_finder(sp_die, pf);
1061 } 1247 }
1062 } else { 1248 } else {
1063 struct dwarf_callback_param _param = {.data = (void *)pf, 1249 struct dwarf_callback_param _param = {.data = (void *)pf,
@@ -1079,90 +1265,276 @@ static int find_probe_point_by_func(struct probe_finder *pf)
1079 return _param.retval; 1265 return _param.retval;
1080} 1266}
1081 1267
1082/* Find probe_trace_events specified by perf_probe_event from debuginfo */ 1268/* Find probe points from debuginfo */
1083int find_probe_trace_events(int fd, struct perf_probe_event *pev, 1269static int find_probes(int fd, struct probe_finder *pf)
1084 struct probe_trace_event **tevs, int max_tevs)
1085{ 1270{
1086 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; 1271 struct perf_probe_point *pp = &pf->pev->point;
1087 struct perf_probe_point *pp = &pev->point;
1088 Dwarf_Off off, noff; 1272 Dwarf_Off off, noff;
1089 size_t cuhl; 1273 size_t cuhl;
1090 Dwarf_Die *diep; 1274 Dwarf_Die *diep;
1091 Dwarf *dbg; 1275 Dwarf *dbg = NULL;
1276 Dwfl *dwfl;
1277 Dwarf_Addr bias; /* Currently ignored */
1092 int ret = 0; 1278 int ret = 0;
1093 1279
1094 pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); 1280 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1095 if (pf.tevs == NULL)
1096 return -ENOMEM;
1097 *tevs = pf.tevs;
1098 pf.ntevs = 0;
1099
1100 dbg = dwarf_begin(fd, DWARF_C_READ);
1101 if (!dbg) { 1281 if (!dbg) {
1102 pr_warning("No dwarf info found in the vmlinux - " 1282 pr_warning("No dwarf info found in the vmlinux - "
1103 "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1283 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1104 free(pf.tevs);
1105 *tevs = NULL;
1106 return -EBADF; 1284 return -EBADF;
1107 } 1285 }
1108 1286
1109#if _ELFUTILS_PREREQ(0, 142) 1287#if _ELFUTILS_PREREQ(0, 142)
1110 /* Get the call frame information from this dwarf */ 1288 /* Get the call frame information from this dwarf */
1111 pf.cfi = dwarf_getcfi(dbg); 1289 pf->cfi = dwarf_getcfi(dbg);
1112#endif 1290#endif
1113 1291
1114 off = 0; 1292 off = 0;
1115 line_list__init(&pf.lcache); 1293 line_list__init(&pf->lcache);
1116 /* Loop on CUs (Compilation Unit) */ 1294 /* Loop on CUs (Compilation Unit) */
1117 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && 1295 while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
1118 ret >= 0) { 1296 ret >= 0) {
1119 /* Get the DIE(Debugging Information Entry) of this CU */ 1297 /* Get the DIE(Debugging Information Entry) of this CU */
1120 diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); 1298 diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
1121 if (!diep) 1299 if (!diep)
1122 continue; 1300 continue;
1123 1301
1124 /* Check if target file is included. */ 1302 /* Check if target file is included. */
1125 if (pp->file) 1303 if (pp->file)
1126 pf.fname = cu_find_realpath(&pf.cu_die, pp->file); 1304 pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
1127 else 1305 else
1128 pf.fname = NULL; 1306 pf->fname = NULL;
1129 1307
1130 if (!pp->file || pf.fname) { 1308 if (!pp->file || pf->fname) {
1131 if (pp->function) 1309 if (pp->function)
1132 ret = find_probe_point_by_func(&pf); 1310 ret = find_probe_point_by_func(pf);
1133 else if (pp->lazy_line) 1311 else if (pp->lazy_line)
1134 ret = find_probe_point_lazy(NULL, &pf); 1312 ret = find_probe_point_lazy(NULL, pf);
1135 else { 1313 else {
1136 pf.lno = pp->line; 1314 pf->lno = pp->line;
1137 ret = find_probe_point_by_line(&pf); 1315 ret = find_probe_point_by_line(pf);
1138 } 1316 }
1139 } 1317 }
1140 off = noff; 1318 off = noff;
1141 } 1319 }
1142 line_list__free(&pf.lcache); 1320 line_list__free(&pf->lcache);
1143 dwarf_end(dbg); 1321 if (dwfl)
1322 dwfl_end(dwfl);
1144 1323
1145 return (ret < 0) ? ret : pf.ntevs; 1324 return ret;
1325}
1326
1327/* Add a found probe point into trace event list */
1328static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
1329{
1330 struct trace_event_finder *tf =
1331 container_of(pf, struct trace_event_finder, pf);
1332 struct probe_trace_event *tev;
1333 int ret, i;
1334
1335 /* Check number of tevs */
1336 if (tf->ntevs == tf->max_tevs) {
1337 pr_warning("Too many( > %d) probe point found.\n",
1338 tf->max_tevs);
1339 return -ERANGE;
1340 }
1341 tev = &tf->tevs[tf->ntevs++];
1342
1343 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1344 &tev->point);
1345 if (ret < 0)
1346 return ret;
1347
1348 pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
1349 tev->point.offset);
1350
1351 /* Find each argument */
1352 tev->nargs = pf->pev->nargs;
1353 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
1354 if (tev->args == NULL)
1355 return -ENOMEM;
1356 for (i = 0; i < pf->pev->nargs; i++) {
1357 pf->pvar = &pf->pev->args[i];
1358 pf->tvar = &tev->args[i];
1359 ret = find_variable(sp_die, pf);
1360 if (ret != 0)
1361 return ret;
1362 }
1363
1364 return 0;
1365}
1366
1367/* Find probe_trace_events specified by perf_probe_event from debuginfo */
1368int find_probe_trace_events(int fd, struct perf_probe_event *pev,
1369 struct probe_trace_event **tevs, int max_tevs)
1370{
1371 struct trace_event_finder tf = {
1372 .pf = {.pev = pev, .callback = add_probe_trace_event},
1373 .max_tevs = max_tevs};
1374 int ret;
1375
1376 /* Allocate result tevs array */
1377 *tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
1378 if (*tevs == NULL)
1379 return -ENOMEM;
1380
1381 tf.tevs = *tevs;
1382 tf.ntevs = 0;
1383
1384 ret = find_probes(fd, &tf.pf);
1385 if (ret < 0) {
1386 free(*tevs);
1387 *tevs = NULL;
1388 return ret;
1389 }
1390
1391 return (ret < 0) ? ret : tf.ntevs;
1392}
1393
1394#define MAX_VAR_LEN 64
1395
1396/* Collect available variables in this scope */
1397static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
1398{
1399 struct available_var_finder *af = data;
1400 struct variable_list *vl;
1401 char buf[MAX_VAR_LEN];
1402 int tag, ret;
1403
1404 vl = &af->vls[af->nvls - 1];
1405
1406 tag = dwarf_tag(die_mem);
1407 if (tag == DW_TAG_formal_parameter ||
1408 tag == DW_TAG_variable) {
1409 ret = convert_variable_location(die_mem, af->pf.addr,
1410 af->pf.fb_ops, NULL);
1411 if (ret == 0) {
1412 ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
1413 pr_debug2("Add new var: %s\n", buf);
1414 if (ret > 0)
1415 strlist__add(vl->vars, buf);
1416 }
1417 }
1418
1419 if (af->child && dwarf_haspc(die_mem, af->pf.addr))
1420 return DIE_FIND_CB_CONTINUE;
1421 else
1422 return DIE_FIND_CB_SIBLING;
1423}
1424
1425/* Add a found vars into available variables list */
1426static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
1427{
1428 struct available_var_finder *af =
1429 container_of(pf, struct available_var_finder, pf);
1430 struct variable_list *vl;
1431 Dwarf_Die die_mem, *scopes = NULL;
1432 int ret, nscopes;
1433
1434 /* Check number of tevs */
1435 if (af->nvls == af->max_vls) {
1436 pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
1437 return -ERANGE;
1438 }
1439 vl = &af->vls[af->nvls++];
1440
1441 ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
1442 &vl->point);
1443 if (ret < 0)
1444 return ret;
1445
1446 pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
1447 vl->point.offset);
1448
1449 /* Find local variables */
1450 vl->vars = strlist__new(true, NULL);
1451 if (vl->vars == NULL)
1452 return -ENOMEM;
1453 af->child = true;
1454 die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
1455
1456 /* Find external variables */
1457 if (!af->externs)
1458 goto out;
1459 /* Don't need to search child DIE for externs. */
1460 af->child = false;
1461 nscopes = dwarf_getscopes_die(sp_die, &scopes);
1462 while (nscopes-- > 1)
1463 die_find_child(&scopes[nscopes], collect_variables_cb,
1464 (void *)af, &die_mem);
1465 if (scopes)
1466 free(scopes);
1467
1468out:
1469 if (strlist__empty(vl->vars)) {
1470 strlist__delete(vl->vars);
1471 vl->vars = NULL;
1472 }
1473
1474 return ret;
1475}
1476
1477/* Find available variables at given probe point */
1478int find_available_vars_at(int fd, struct perf_probe_event *pev,
1479 struct variable_list **vls, int max_vls,
1480 bool externs)
1481{
1482 struct available_var_finder af = {
1483 .pf = {.pev = pev, .callback = add_available_vars},
1484 .max_vls = max_vls, .externs = externs};
1485 int ret;
1486
1487 /* Allocate result vls array */
1488 *vls = zalloc(sizeof(struct variable_list) * max_vls);
1489 if (*vls == NULL)
1490 return -ENOMEM;
1491
1492 af.vls = *vls;
1493 af.nvls = 0;
1494
1495 ret = find_probes(fd, &af.pf);
1496 if (ret < 0) {
1497 /* Free vlist for error */
1498 while (af.nvls--) {
1499 if (af.vls[af.nvls].point.symbol)
1500 free(af.vls[af.nvls].point.symbol);
1501 if (af.vls[af.nvls].vars)
1502 strlist__delete(af.vls[af.nvls].vars);
1503 }
1504 free(af.vls);
1505 *vls = NULL;
1506 return ret;
1507 }
1508
1509 return (ret < 0) ? ret : af.nvls;
1146} 1510}
1147 1511
1148/* Reverse search */ 1512/* Reverse search */
1149int find_perf_probe_point(int fd, unsigned long addr, 1513int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
1150 struct perf_probe_point *ppt)
1151{ 1514{
1152 Dwarf_Die cudie, spdie, indie; 1515 Dwarf_Die cudie, spdie, indie;
1153 Dwarf *dbg; 1516 Dwarf *dbg = NULL;
1517 Dwfl *dwfl = NULL;
1154 Dwarf_Line *line; 1518 Dwarf_Line *line;
1155 Dwarf_Addr laddr, eaddr; 1519 Dwarf_Addr laddr, eaddr, bias = 0;
1156 const char *tmp; 1520 const char *tmp;
1157 int lineno, ret = 0; 1521 int lineno, ret = 0;
1158 bool found = false; 1522 bool found = false;
1159 1523
1160 dbg = dwarf_begin(fd, DWARF_C_READ); 1524 /* Open the live linux kernel */
1161 if (!dbg) 1525 dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
1162 return -EBADF; 1526 if (!dbg) {
1527 pr_warning("No dwarf info found in the vmlinux - "
1528 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
1529 ret = -EINVAL;
1530 goto end;
1531 }
1163 1532
1533 /* Adjust address with bias */
1534 addr += bias;
1164 /* Find cu die */ 1535 /* Find cu die */
1165 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { 1536 if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
1537 pr_warning("No CU DIE is found at %lx\n", addr);
1166 ret = -EINVAL; 1538 ret = -EINVAL;
1167 goto end; 1539 goto end;
1168 } 1540 }
@@ -1225,7 +1597,8 @@ found:
1225 } 1597 }
1226 1598
1227end: 1599end:
1228 dwarf_end(dbg); 1600 if (dwfl)
1601 dwfl_end(dwfl);
1229 if (ret >= 0) 1602 if (ret >= 0)
1230 ret = found ? 1 : 0; 1603 ret = found ? 1 : 0;
1231 return ret; 1604 return ret;
@@ -1358,6 +1731,9 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1358 struct line_finder *lf = param->data; 1731 struct line_finder *lf = param->data;
1359 struct line_range *lr = lf->lr; 1732 struct line_range *lr = lf->lr;
1360 1733
1734 pr_debug("find (%llx) %s\n",
1735 (unsigned long long)dwarf_dieoffset(sp_die),
1736 dwarf_diename(sp_die));
1361 if (dwarf_tag(sp_die) == DW_TAG_subprogram && 1737 if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
1362 die_compare_name(sp_die, lr->function)) { 1738 die_compare_name(sp_die, lr->function)) {
1363 lf->fname = dwarf_decl_file(sp_die); 1739 lf->fname = dwarf_decl_file(sp_die);
@@ -1401,10 +1777,12 @@ int find_line_range(int fd, struct line_range *lr)
1401 Dwarf_Off off = 0, noff; 1777 Dwarf_Off off = 0, noff;
1402 size_t cuhl; 1778 size_t cuhl;
1403 Dwarf_Die *diep; 1779 Dwarf_Die *diep;
1404 Dwarf *dbg; 1780 Dwarf *dbg = NULL;
1781 Dwfl *dwfl;
1782 Dwarf_Addr bias; /* Currently ignored */
1405 const char *comp_dir; 1783 const char *comp_dir;
1406 1784
1407 dbg = dwarf_begin(fd, DWARF_C_READ); 1785 dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
1408 if (!dbg) { 1786 if (!dbg) {
1409 pr_warning("No dwarf info found in the vmlinux - " 1787 pr_warning("No dwarf info found in the vmlinux - "
1410 "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1788 "please rebuild with CONFIG_DEBUG_INFO=y.\n");
@@ -1450,8 +1828,7 @@ int find_line_range(int fd, struct line_range *lr)
1450 } 1828 }
1451 1829
1452 pr_debug("path: %s\n", lr->path); 1830 pr_debug("path: %s\n", lr->path);
1453 dwarf_end(dbg); 1831 dwfl_end(dwfl);
1454
1455 return (ret < 0) ? ret : lf.found; 1832 return (ret < 0) ? ret : lf.found;
1456} 1833}
1457 1834
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/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/symbol.c b/tools/perf/util/symbol.c
index b2f5ae97f33d..b39f499e575a 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -388,6 +388,20 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
388 return fprintf(fp, "%s", sbuild_id); 388 return fprintf(fp, "%s", sbuild_id);
389} 389}
390 390
391size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
392{
393 size_t ret = 0;
394 struct rb_node *nd;
395 struct symbol_name_rb_node *pos;
396
397 for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
398 pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
399 fprintf(fp, "%s\n", pos->sym.name);
400 }
401
402 return ret;
403}
404
391size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) 405size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
392{ 406{
393 struct rb_node *nd; 407 struct rb_node *nd;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index ea95c2756f05..038f2201ee09 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -182,6 +182,7 @@ size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
182size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits); 182size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
183 183
184size_t dso__fprintf_buildid(struct dso *self, FILE *fp); 184size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
185size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
185size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); 186size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
186 187
187enum dso_origin { 188enum dso_origin {
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
index 66f2d583d8c4..8bc010edca25 100644
--- a/tools/perf/util/ui/browser.c
+++ b/tools/perf/util/ui/browser.c
@@ -1,16 +1,5 @@
1#define _GNU_SOURCE 1#include "libslang.h"
2#include <stdio.h> 2#include <linux/compiler.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 <linux/list.h> 3#include <linux/list.h>
15#include <linux/rbtree.h> 4#include <linux/rbtree.h>
16#include <stdlib.h> 5#include <stdlib.h>
@@ -19,17 +8,9 @@
19#include "helpline.h" 8#include "helpline.h"
20#include "../color.h" 9#include "../color.h"
21#include "../util.h" 10#include "../util.h"
11#include <stdio.h>
22 12
23#if SLANG_VERSION < 20104 13static int ui_browser__percent_color(double percent, bool current)
24#define sltt_set_color(obj, name, fg, bg) \
25 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
26#else
27#define sltt_set_color SLtt_set_color
28#endif
29
30newtComponent newt_form__new(void);
31
32int ui_browser__percent_color(double percent, bool current)
33{ 14{
34 if (current) 15 if (current)
35 return HE_COLORSET_SELECTED; 16 return HE_COLORSET_SELECTED;
@@ -40,6 +21,23 @@ int ui_browser__percent_color(double percent, bool current)
40 return HE_COLORSET_NORMAL; 21 return HE_COLORSET_NORMAL;
41} 22}
42 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
43void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) 41void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
44{ 42{
45 struct list_head *head = self->entries; 43 struct list_head *head = self->entries;
@@ -111,7 +109,7 @@ unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
111 nd = self->top; 109 nd = self->top;
112 110
113 while (nd != NULL) { 111 while (nd != NULL) {
114 SLsmg_gotorc(self->y + row, self->x); 112 ui_browser__gotorc(self, row, 0);
115 self->write(self, nd, row); 113 self->write(self, nd, row);
116 if (++row == self->height) 114 if (++row == self->height)
117 break; 115 break;
@@ -131,13 +129,10 @@ void ui_browser__refresh_dimensions(struct ui_browser *self)
131 int cols, rows; 129 int cols, rows;
132 newtGetScreenSize(&cols, &rows); 130 newtGetScreenSize(&cols, &rows);
133 131
134 if (self->width > cols - 4) 132 self->width = cols - 1;
135 self->width = cols - 4; 133 self->height = rows - 2;
136 self->height = rows - 5; 134 self->y = 1;
137 if (self->height > self->nr_entries) 135 self->x = 0;
138 self->height = self->nr_entries;
139 self->y = (rows - self->height) / 2;
140 self->x = (cols - self->width) / 2;
141} 136}
142 137
143void ui_browser__reset_index(struct ui_browser *self) 138void ui_browser__reset_index(struct ui_browser *self)
@@ -146,34 +141,48 @@ void ui_browser__reset_index(struct ui_browser *self)
146 self->seek(self, 0, SEEK_SET); 141 self->seek(self, 0, SEEK_SET);
147} 142}
148 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
149int ui_browser__show(struct ui_browser *self, const char *title, 159int ui_browser__show(struct ui_browser *self, const char *title,
150 const char *helpline, ...) 160 const char *helpline, ...)
151{ 161{
152 va_list ap; 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 };
153 166
154 if (self->form != NULL) { 167 if (self->form != NULL)
155 newtFormDestroy(self->form); 168 newtFormDestroy(self->form);
156 newtPopWindow(); 169
157 }
158 ui_browser__refresh_dimensions(self); 170 ui_browser__refresh_dimensions(self);
159 newtCenteredWindow(self->width, self->height, title); 171 self->form = newtForm(NULL, NULL, 0);
160 self->form = newt_form__new();
161 if (self->form == NULL) 172 if (self->form == NULL)
162 return -1; 173 return -1;
163 174
164 self->sb = newtVerticalScrollbar(self->width, 0, self->height, 175 self->sb = newtVerticalScrollbar(self->width, 1, self->height,
165 HE_COLORSET_NORMAL, 176 HE_COLORSET_NORMAL,
166 HE_COLORSET_SELECTED); 177 HE_COLORSET_SELECTED);
167 if (self->sb == NULL) 178 if (self->sb == NULL)
168 return -1; 179 return -1;
169 180
170 newtFormAddHotKey(self->form, NEWT_KEY_UP); 181 SLsmg_gotorc(0, 0);
171 newtFormAddHotKey(self->form, NEWT_KEY_DOWN); 182 ui_browser__set_color(self, NEWT_COLORSET_ROOT);
172 newtFormAddHotKey(self->form, NEWT_KEY_PGUP); 183 slsmg_write_nstring(title, self->width);
173 newtFormAddHotKey(self->form, NEWT_KEY_PGDN); 184
174 newtFormAddHotKey(self->form, NEWT_KEY_HOME); 185 ui_browser__add_exit_keys(self, keys);
175 newtFormAddHotKey(self->form, NEWT_KEY_END);
176 newtFormAddHotKey(self->form, ' ');
177 newtFormAddComponent(self->form, self->sb); 186 newtFormAddComponent(self->form, self->sb);
178 187
179 va_start(ap, helpline); 188 va_start(ap, helpline);
@@ -185,7 +194,6 @@ int ui_browser__show(struct ui_browser *self, const char *title,
185void ui_browser__hide(struct ui_browser *self) 194void ui_browser__hide(struct ui_browser *self)
186{ 195{
187 newtFormDestroy(self->form); 196 newtFormDestroy(self->form);
188 newtPopWindow();
189 self->form = NULL; 197 self->form = NULL;
190 ui_helpline__pop(); 198 ui_helpline__pop();
191} 199}
@@ -196,28 +204,28 @@ int ui_browser__refresh(struct ui_browser *self)
196 204
197 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); 205 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
198 row = self->refresh(self); 206 row = self->refresh(self);
199 SLsmg_set_color(HE_COLORSET_NORMAL); 207 ui_browser__set_color(self, HE_COLORSET_NORMAL);
200 SLsmg_fill_region(self->y + row, self->x, 208 SLsmg_fill_region(self->y + row, self->x,
201 self->height - row, self->width, ' '); 209 self->height - row, self->width, ' ');
202 210
203 return 0; 211 return 0;
204} 212}
205 213
206int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) 214int ui_browser__run(struct ui_browser *self)
207{ 215{
216 struct newtExitStruct es;
217
208 if (ui_browser__refresh(self) < 0) 218 if (ui_browser__refresh(self) < 0)
209 return -1; 219 return -1;
210 220
211 while (1) { 221 while (1) {
212 off_t offset; 222 off_t offset;
213 223
214 newtFormRun(self->form, es); 224 newtFormRun(self->form, &es);
215 225
216 if (es->reason != NEWT_EXIT_HOTKEY) 226 if (es.reason != NEWT_EXIT_HOTKEY)
217 break; 227 break;
218 if (is_exit_key(es->u.key)) 228 switch (es.u.key) {
219 return es->u.key;
220 switch (es->u.key) {
221 case NEWT_KEY_DOWN: 229 case NEWT_KEY_DOWN:
222 if (self->index == self->nr_entries - 1) 230 if (self->index == self->nr_entries - 1)
223 break; 231 break;
@@ -274,12 +282,12 @@ int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
274 self->seek(self, -offset, SEEK_END); 282 self->seek(self, -offset, SEEK_END);
275 break; 283 break;
276 default: 284 default:
277 return es->u.key; 285 return es.u.key;
278 } 286 }
279 if (ui_browser__refresh(self) < 0) 287 if (ui_browser__refresh(self) < 0)
280 return -1; 288 return -1;
281 } 289 }
282 return 0; 290 return -1;
283} 291}
284 292
285unsigned int ui_browser__list_head_refresh(struct ui_browser *self) 293unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
@@ -294,7 +302,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
294 pos = self->top; 302 pos = self->top;
295 303
296 list_for_each_from(pos, head) { 304 list_for_each_from(pos, head) {
297 SLsmg_gotorc(self->y + row, self->x); 305 ui_browser__gotorc(self, row, 0);
298 self->write(self, pos, row); 306 self->write(self, pos, row);
299 if (++row == self->height) 307 if (++row == self->height)
300 break; 308 break;
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
index 0b9f829214f7..0dc7e4da36f5 100644
--- a/tools/perf/util/ui/browser.h
+++ b/tools/perf/util/ui/browser.h
@@ -25,16 +25,21 @@ struct ui_browser {
25}; 25};
26 26
27 27
28int ui_browser__percent_color(double percent, bool current); 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);
29bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); 31bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
30void ui_browser__refresh_dimensions(struct ui_browser *self); 32void ui_browser__refresh_dimensions(struct ui_browser *self);
31void ui_browser__reset_index(struct ui_browser *self); 33void ui_browser__reset_index(struct ui_browser *self);
32 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[]);
33int ui_browser__show(struct ui_browser *self, const char *title, 38int ui_browser__show(struct ui_browser *self, const char *title,
34 const char *helpline, ...); 39 const char *helpline, ...);
35void ui_browser__hide(struct ui_browser *self); 40void ui_browser__hide(struct ui_browser *self);
36int ui_browser__refresh(struct ui_browser *self); 41int ui_browser__refresh(struct ui_browser *self);
37int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es); 42int ui_browser__run(struct ui_browser *self);
38 43
39void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); 44void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
40unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); 45unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
index a90273e63f4f..82b78f99251b 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -40,14 +40,12 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
40 40
41 if (ol->offset != -1) { 41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol); 42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 int color = ui_browser__percent_color(olrb->percent, current_entry); 43 ui_browser__set_percent_color(self, olrb->percent, current_entry);
44 SLsmg_set_color(color);
45 slsmg_printf(" %7.2f ", olrb->percent); 44 slsmg_printf(" %7.2f ", olrb->percent);
46 if (!current_entry) 45 if (!current_entry)
47 SLsmg_set_color(HE_COLORSET_CODE); 46 ui_browser__set_color(self, HE_COLORSET_CODE);
48 } else { 47 } else {
49 int color = ui_browser__percent_color(0, current_entry); 48 ui_browser__set_percent_color(self, 0, current_entry);
50 SLsmg_set_color(color);
51 slsmg_write_nstring(" ", 9); 49 slsmg_write_nstring(" ", 9);
52 } 50 }
53 51
@@ -135,32 +133,31 @@ static void annotate_browser__set_top(struct annotate_browser *self,
135 self->curr_hot = nd; 133 self->curr_hot = nd;
136} 134}
137 135
138static int annotate_browser__run(struct annotate_browser *self, 136static int annotate_browser__run(struct annotate_browser *self)
139 struct newtExitStruct *es)
140{ 137{
141 struct rb_node *nd; 138 struct rb_node *nd;
142 struct hist_entry *he = self->b.priv; 139 struct hist_entry *he = self->b.priv;
140 int key;
143 141
144 if (ui_browser__show(&self->b, he->ms.sym->name, 142 if (ui_browser__show(&self->b, he->ms.sym->name,
145 "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) 143 "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
146 return -1; 144 return -1;
147 145 /*
148 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); 146 * To allow builtin-annotate to cycle thru multiple symbols by
149 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); 147 * examining the exit key for this function.
148 */
149 ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT);
150 150
151 nd = self->curr_hot; 151 nd = self->curr_hot;
152 if (nd) { 152 if (nd) {
153 newtFormAddHotKey(self->b.form, NEWT_KEY_TAB); 153 int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 };
154 newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB); 154 ui_browser__add_exit_keys(&self->b, tabs);
155 } 155 }
156 156
157 while (1) { 157 while (1) {
158 ui_browser__run(&self->b, es); 158 key = ui_browser__run(&self->b);
159
160 if (es->reason != NEWT_EXIT_HOTKEY)
161 break;
162 159
163 switch (es->u.key) { 160 switch (key) {
164 case NEWT_KEY_TAB: 161 case NEWT_KEY_TAB:
165 nd = rb_prev(nd); 162 nd = rb_prev(nd);
166 if (nd == NULL) 163 if (nd == NULL)
@@ -179,12 +176,11 @@ static int annotate_browser__run(struct annotate_browser *self,
179 } 176 }
180out: 177out:
181 ui_browser__hide(&self->b); 178 ui_browser__hide(&self->b);
182 return es->u.key; 179 return key;
183} 180}
184 181
185int hist_entry__tui_annotate(struct hist_entry *self) 182int hist_entry__tui_annotate(struct hist_entry *self)
186{ 183{
187 struct newtExitStruct es;
188 struct objdump_line *pos, *n; 184 struct objdump_line *pos, *n;
189 struct objdump_line_rb_node *rbpos; 185 struct objdump_line_rb_node *rbpos;
190 LIST_HEAD(head); 186 LIST_HEAD(head);
@@ -232,7 +228,7 @@ int hist_entry__tui_annotate(struct hist_entry *self)
232 annotate_browser__set_top(&browser, browser.curr_hot); 228 annotate_browser__set_top(&browser, browser.curr_hot);
233 229
234 browser.b.width += 18; /* Percentage */ 230 browser.b.width += 18; /* Percentage */
235 ret = annotate_browser__run(&browser, &es); 231 ret = annotate_browser__run(&browser);
236 list_for_each_entry_safe(pos, n, &head, node) { 232 list_for_each_entry_safe(pos, n, &head, node) {
237 list_del(&pos->node); 233 list_del(&pos->node);
238 objdump_line__free(pos); 234 objdump_line__free(pos);
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index 6866aa4c41e0..ebda8c3fde9e 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -58,6 +58,11 @@ static char callchain_list__folded(const struct callchain_list *self)
58 return map_symbol__folded(&self->ms); 58 return map_symbol__folded(&self->ms);
59} 59}
60 60
61static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
62{
63 self->unfolded = unfold ? self->has_children : false;
64}
65
61static int callchain_node__count_rows_rb_tree(struct callchain_node *self) 66static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
62{ 67{
63 int n = 0; 68 int n = 0;
@@ -129,16 +134,16 @@ static void callchain_node__init_have_children_rb_tree(struct callchain_node *se
129 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { 134 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
130 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); 135 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
131 struct callchain_list *chain; 136 struct callchain_list *chain;
132 int first = true; 137 bool first = true;
133 138
134 list_for_each_entry(chain, &child->val, list) { 139 list_for_each_entry(chain, &child->val, list) {
135 if (first) { 140 if (first) {
136 first = false; 141 first = false;
137 chain->ms.has_children = chain->list.next != &child->val || 142 chain->ms.has_children = chain->list.next != &child->val ||
138 rb_first(&child->rb_root) != NULL; 143 !RB_EMPTY_ROOT(&child->rb_root);
139 } else 144 } else
140 chain->ms.has_children = chain->list.next == &child->val && 145 chain->ms.has_children = chain->list.next == &child->val &&
141 rb_first(&child->rb_root) != NULL; 146 !RB_EMPTY_ROOT(&child->rb_root);
142 } 147 }
143 148
144 callchain_node__init_have_children_rb_tree(child); 149 callchain_node__init_have_children_rb_tree(child);
@@ -150,7 +155,7 @@ static void callchain_node__init_have_children(struct callchain_node *self)
150 struct callchain_list *chain; 155 struct callchain_list *chain;
151 156
152 list_for_each_entry(chain, &self->val, list) 157 list_for_each_entry(chain, &self->val, list)
153 chain->ms.has_children = rb_first(&self->rb_root) != NULL; 158 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
154 159
155 callchain_node__init_have_children_rb_tree(self); 160 callchain_node__init_have_children_rb_tree(self);
156} 161}
@@ -168,6 +173,7 @@ static void callchain__init_have_children(struct rb_root *self)
168static void hist_entry__init_have_children(struct hist_entry *self) 173static void hist_entry__init_have_children(struct hist_entry *self)
169{ 174{
170 if (!self->init_have_children) { 175 if (!self->init_have_children) {
176 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
171 callchain__init_have_children(&self->sorted_chain); 177 callchain__init_have_children(&self->sorted_chain);
172 self->init_have_children = true; 178 self->init_have_children = true;
173 } 179 }
@@ -195,43 +201,114 @@ static bool hist_browser__toggle_fold(struct hist_browser *self)
195 return false; 201 return false;
196} 202}
197 203
198static int hist_browser__run(struct hist_browser *self, const char *title, 204static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
199 struct newtExitStruct *es) 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)
200{ 292{
201 char str[256], unit; 293 int key;
202 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; 294 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
295 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, };
203 296
204 self->b.entries = &self->hists->entries; 297 self->b.entries = &self->hists->entries;
205 self->b.nr_entries = self->hists->nr_entries; 298 self->b.nr_entries = self->hists->nr_entries;
206 299
207 hist_browser__refresh_dimensions(self); 300 hist_browser__refresh_dimensions(self);
208 301
209 nr_events = convert_unit(nr_events, &unit);
210 snprintf(str, sizeof(str), "Events: %lu%c ",
211 nr_events, unit);
212 newtDrawRootText(0, 0, str);
213
214 if (ui_browser__show(&self->b, title, 302 if (ui_browser__show(&self->b, title,
215 "Press '?' for help on key bindings") < 0) 303 "Press '?' for help on key bindings") < 0)
216 return -1; 304 return -1;
217 305
218 newtFormAddHotKey(self->b.form, 'a'); 306 ui_browser__add_exit_keys(&self->b, exit_keys);
219 newtFormAddHotKey(self->b.form, '?');
220 newtFormAddHotKey(self->b.form, 'h');
221 newtFormAddHotKey(self->b.form, 'd');
222 newtFormAddHotKey(self->b.form, 'D');
223 newtFormAddHotKey(self->b.form, 't');
224
225 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
226 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
227 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
228 307
229 while (1) { 308 while (1) {
230 ui_browser__run(&self->b, es); 309 key = ui_browser__run(&self->b);
231 310
232 if (es->reason != NEWT_EXIT_HOTKEY) 311 switch (key) {
233 break;
234 switch (es->u.key) {
235 case 'D': { /* Debug */ 312 case 'D': { /* Debug */
236 static int seq; 313 static int seq;
237 struct hist_entry *h = rb_entry(self->b.top, 314 struct hist_entry *h = rb_entry(self->b.top,
@@ -245,18 +322,26 @@ static int hist_browser__run(struct hist_browser *self, const char *title,
245 self->b.top_idx, 322 self->b.top_idx,
246 h->row_offset, h->nr_rows); 323 h->row_offset, h->nr_rows);
247 } 324 }
248 continue; 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;
249 case NEWT_KEY_ENTER: 334 case NEWT_KEY_ENTER:
250 if (hist_browser__toggle_fold(self)) 335 if (hist_browser__toggle_fold(self))
251 break; 336 break;
252 /* fall thru */ 337 /* fall thru */
253 default: 338 default:
254 return 0; 339 goto out;
255 } 340 }
256 } 341 }
257 342out:
258 ui_browser__hide(&self->b); 343 ui_browser__hide(&self->b);
259 return 0; 344 return key;
260} 345}
261 346
262static char *callchain_list__sym_name(struct callchain_list *self, 347static char *callchain_list__sym_name(struct callchain_list *self,
@@ -306,15 +391,10 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
306 int color; 391 int color;
307 bool was_first = first; 392 bool was_first = first;
308 393
309 if (first) { 394 if (first)
310 first = false; 395 first = false;
311 chain->ms.has_children = chain->list.next != &child->val || 396 else
312 rb_first(&child->rb_root) != NULL;
313 } else {
314 extra_offset = LEVEL_OFFSET_STEP; 397 extra_offset = LEVEL_OFFSET_STEP;
315 chain->ms.has_children = chain->list.next == &child->val &&
316 rb_first(&child->rb_root) != NULL;
317 }
318 398
319 folded_sign = callchain_list__folded(chain); 399 folded_sign = callchain_list__folded(chain);
320 if (*row_offset != 0) { 400 if (*row_offset != 0) {
@@ -341,8 +421,8 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
341 *is_current_entry = true; 421 *is_current_entry = true;
342 } 422 }
343 423
344 SLsmg_set_color(color); 424 ui_browser__set_color(&self->b, color);
345 SLsmg_gotorc(self->b.y + row, self->b.x); 425 ui_browser__gotorc(&self->b, row, 0);
346 slsmg_write_nstring(" ", offset + extra_offset); 426 slsmg_write_nstring(" ", offset + extra_offset);
347 slsmg_printf("%c ", folded_sign); 427 slsmg_printf("%c ", folded_sign);
348 slsmg_write_nstring(str, width); 428 slsmg_write_nstring(str, width);
@@ -384,12 +464,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
384 list_for_each_entry(chain, &node->val, list) { 464 list_for_each_entry(chain, &node->val, list) {
385 char ipstr[BITS_PER_LONG / 4 + 1], *s; 465 char ipstr[BITS_PER_LONG / 4 + 1], *s;
386 int color; 466 int color;
387 /* 467
388 * FIXME: This should be moved to somewhere else,
389 * probably when the callchain is created, so as not to
390 * traverse it all over again
391 */
392 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
393 folded_sign = callchain_list__folded(chain); 468 folded_sign = callchain_list__folded(chain);
394 469
395 if (*row_offset != 0) { 470 if (*row_offset != 0) {
@@ -405,8 +480,8 @@ static int hist_browser__show_callchain_node(struct hist_browser *self,
405 } 480 }
406 481
407 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); 482 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
408 SLsmg_gotorc(self->b.y + row, self->b.x); 483 ui_browser__gotorc(&self->b, row, 0);
409 SLsmg_set_color(color); 484 ui_browser__set_color(&self->b, color);
410 slsmg_write_nstring(" ", offset); 485 slsmg_write_nstring(" ", offset);
411 slsmg_printf("%c ", folded_sign); 486 slsmg_printf("%c ", folded_sign);
412 slsmg_write_nstring(s, width - 2); 487 slsmg_write_nstring(s, width - 2);
@@ -465,7 +540,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
465 } 540 }
466 541
467 if (symbol_conf.use_callchain) { 542 if (symbol_conf.use_callchain) {
468 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); 543 hist_entry__init_have_children(entry);
469 folded_sign = hist_entry__folded(entry); 544 folded_sign = hist_entry__folded(entry);
470 } 545 }
471 546
@@ -484,8 +559,8 @@ static int hist_browser__show_entry(struct hist_browser *self,
484 color = HE_COLORSET_NORMAL; 559 color = HE_COLORSET_NORMAL;
485 } 560 }
486 561
487 SLsmg_set_color(color); 562 ui_browser__set_color(&self->b, color);
488 SLsmg_gotorc(self->b.y + row, self->b.x); 563 ui_browser__gotorc(&self->b, row, 0);
489 if (symbol_conf.use_callchain) { 564 if (symbol_conf.use_callchain) {
490 slsmg_printf("%c ", folded_sign); 565 slsmg_printf("%c ", folded_sign);
491 width -= 2; 566 width -= 2;
@@ -687,8 +762,6 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
687 762
688static void hist_browser__delete(struct hist_browser *self) 763static void hist_browser__delete(struct hist_browser *self)
689{ 764{
690 newtFormDestroy(self->b.form);
691 newtPopWindow();
692 free(self); 765 free(self);
693} 766}
694 767
@@ -702,21 +775,26 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
702 return self->he_selection->thread; 775 return self->he_selection->thread;
703} 776}
704 777
705static int hist_browser__title(char *bf, size_t size, const char *ev_name, 778static int hists__browser_title(struct hists *self, char *bf, size_t size,
706 const struct dso *dso, const struct thread *thread) 779 const char *ev_name, const struct dso *dso,
780 const struct thread *thread)
707{ 781{
708 int printed = 0; 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);
709 788
710 if (thread) 789 if (thread)
711 printed += snprintf(bf + printed, size - printed, 790 printed += snprintf(bf + printed, size - printed,
712 "Thread: %s(%d)", 791 ", Thread: %s(%d)",
713 (thread->comm_set ? thread->comm : ""), 792 (thread->comm_set ? thread->comm : ""),
714 thread->pid); 793 thread->pid);
715 if (dso) 794 if (dso)
716 printed += snprintf(bf + printed, size - printed, 795 printed += snprintf(bf + printed, size - printed,
717 "%sDSO: %s", thread ? " " : "", 796 ", DSO: %s", dso->short_name);
718 dso->short_name); 797 return printed;
719 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
720} 798}
721 799
722int hists__browse(struct hists *self, const char *helpline, const char *ev_name) 800int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
@@ -725,7 +803,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
725 struct pstack *fstack; 803 struct pstack *fstack;
726 const struct thread *thread_filter = NULL; 804 const struct thread *thread_filter = NULL;
727 const struct dso *dso_filter = NULL; 805 const struct dso *dso_filter = NULL;
728 struct newtExitStruct es;
729 char msg[160]; 806 char msg[160];
730 int key = -1; 807 int key = -1;
731 808
@@ -738,9 +815,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
738 815
739 ui_helpline__push(helpline); 816 ui_helpline__push(helpline);
740 817
741 hist_browser__title(msg, sizeof(msg), ev_name, 818 hists__browser_title(self, msg, sizeof(msg), ev_name,
742 dso_filter, thread_filter); 819 dso_filter, thread_filter);
743
744 while (1) { 820 while (1) {
745 const struct thread *thread; 821 const struct thread *thread;
746 const struct dso *dso; 822 const struct dso *dso;
@@ -749,70 +825,63 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
749 annotate = -2, zoom_dso = -2, zoom_thread = -2, 825 annotate = -2, zoom_dso = -2, zoom_thread = -2,
750 browse_map = -2; 826 browse_map = -2;
751 827
752 if (hist_browser__run(browser, msg, &es)) 828 key = hist_browser__run(browser, msg);
753 break;
754 829
755 thread = hist_browser__selected_thread(browser); 830 thread = hist_browser__selected_thread(browser);
756 dso = browser->selection->map ? browser->selection->map->dso : NULL; 831 dso = browser->selection->map ? browser->selection->map->dso : NULL;
757 832
758 if (es.reason == NEWT_EXIT_HOTKEY) { 833 switch (key) {
759 key = es.u.key; 834 case NEWT_KEY_TAB:
760 835 case NEWT_KEY_UNTAB:
761 switch (key) { 836 /*
762 case NEWT_KEY_F1: 837 * Exit the browser, let hists__browser_tree
763 goto do_help; 838 * go to the next or previous
764 case NEWT_KEY_TAB: 839 */
765 case NEWT_KEY_UNTAB: 840 goto out_free_stack;
766 /* 841 case 'a':
767 * Exit the browser, let hists__browser_tree 842 if (browser->selection->map == NULL &&
768 * go to the next or previous 843 browser->selection->map->dso->annotate_warned)
769 */
770 goto out_free_stack;
771 default:;
772 }
773
774 switch (key) {
775 case 'a':
776 if (browser->selection->map == NULL ||
777 browser->selection->map->dso->annotate_warned)
778 continue;
779 goto do_annotate;
780 case 'd':
781 goto zoom_dso;
782 case 't':
783 goto zoom_thread;
784 case 'h':
785 case '?':
786do_help:
787 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
788 "<- Zoom out\n"
789 "a Annotate current symbol\n"
790 "h/?/F1 Show this window\n"
791 "d Zoom into current DSO\n"
792 "t Zoom into current Thread\n"
793 "q/CTRL+C Exit browser");
794 continue; 844 continue;
795 default:; 845 goto do_annotate;
796 } 846 case 'd':
797 if (is_exit_key(key)) { 847 goto zoom_dso;
798 if (key == NEWT_KEY_ESCAPE && 848 case 't':
799 !ui__dialog_yesno("Do you really want to exit?")) 849 goto zoom_thread;
800 continue; 850 case NEWT_KEY_F1:
801 break; 851 case 'h':
802 } 852 case '?':
803 853 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
804 if (es.u.key == NEWT_KEY_LEFT) { 854 "<- Zoom out\n"
805 const void *top; 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;
806 869
807 if (pstack__empty(fstack)) 870 if (pstack__empty(fstack))
808 continue;
809 top = pstack__pop(fstack);
810 if (top == &dso_filter)
811 goto zoom_out_dso;
812 if (top == &thread_filter)
813 goto zoom_out_thread;
814 continue; 871 continue;
815 } 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;
816 } 885 }
817 886
818 if (browser->selection->sym != NULL && 887 if (browser->selection->sym != NULL &&
@@ -885,8 +954,8 @@ zoom_out_dso:
885 pstack__push(fstack, &dso_filter); 954 pstack__push(fstack, &dso_filter);
886 } 955 }
887 hists__filter_by_dso(self, dso_filter); 956 hists__filter_by_dso(self, dso_filter);
888 hist_browser__title(msg, sizeof(msg), ev_name, 957 hists__browser_title(self, msg, sizeof(msg), ev_name,
889 dso_filter, thread_filter); 958 dso_filter, thread_filter);
890 hist_browser__reset(browser); 959 hist_browser__reset(browser);
891 } else if (choice == zoom_thread) { 960 } else if (choice == zoom_thread) {
892zoom_thread: 961zoom_thread:
@@ -903,8 +972,8 @@ zoom_out_thread:
903 pstack__push(fstack, &thread_filter); 972 pstack__push(fstack, &thread_filter);
904 } 973 }
905 hists__filter_by_thread(self, thread_filter); 974 hists__filter_by_thread(self, thread_filter);
906 hist_browser__title(msg, sizeof(msg), ev_name, 975 hists__browser_title(self, msg, sizeof(msg), ev_name,
907 dso_filter, thread_filter); 976 dso_filter, thread_filter);
908 hist_browser__reset(browser); 977 hist_browser__reset(browser);
909 } 978 }
910 } 979 }
@@ -925,10 +994,6 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help)
925 const char *ev_name = __event_name(hists->type, hists->config); 994 const char *ev_name = __event_name(hists->type, hists->config);
926 995
927 key = hists__browse(hists, help, ev_name); 996 key = hists__browse(hists, help, ev_name);
928
929 if (is_exit_key(key))
930 break;
931
932 switch (key) { 997 switch (key) {
933 case NEWT_KEY_TAB: 998 case NEWT_KEY_TAB:
934 next = rb_next(nd); 999 next = rb_next(nd);
@@ -940,7 +1005,7 @@ int hists__tui_browse_tree(struct rb_root *self, const char *help)
940 continue; 1005 continue;
941 nd = rb_prev(nd); 1006 nd = rb_prev(nd);
942 default: 1007 default:
943 break; 1008 return key;
944 } 1009 }
945 } 1010 }
946 1011
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
index 142b825b42bf..e35437dfa5b4 100644
--- a/tools/perf/util/ui/browsers/map.c
+++ b/tools/perf/util/ui/browsers/map.c
@@ -1,6 +1,5 @@
1#include "../libslang.h" 1#include "../libslang.h"
2#include <elf.h> 2#include <elf.h>
3#include <newt.h>
4#include <sys/ttydefaults.h> 3#include <sys/ttydefaults.h>
5#include <ctype.h> 4#include <ctype.h>
6#include <string.h> 5#include <string.h>
@@ -47,7 +46,6 @@ out_free_form:
47struct map_browser { 46struct map_browser {
48 struct ui_browser b; 47 struct ui_browser b;
49 struct map *map; 48 struct map *map;
50 u16 namelen;
51 u8 addrlen; 49 u8 addrlen;
52}; 50};
53 51
@@ -56,14 +54,16 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row)
56 struct symbol *sym = rb_entry(nd, struct symbol, rb_node); 54 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
57 struct map_browser *mb = container_of(self, struct map_browser, b); 55 struct map_browser *mb = container_of(self, struct map_browser, b);
58 bool current_entry = ui_browser__is_current_entry(self, row); 56 bool current_entry = ui_browser__is_current_entry(self, row);
59 int color = ui_browser__percent_color(0, current_entry); 57 int width;
60 58
61 SLsmg_set_color(color); 59 ui_browser__set_percent_color(self, 0, current_entry);
62 slsmg_printf("%*llx %*llx %c ", 60 slsmg_printf("%*llx %*llx %c ",
63 mb->addrlen, sym->start, mb->addrlen, sym->end, 61 mb->addrlen, sym->start, mb->addrlen, sym->end,
64 sym->binding == STB_GLOBAL ? 'g' : 62 sym->binding == STB_GLOBAL ? 'g' :
65 sym->binding == STB_LOCAL ? 'l' : 'w'); 63 sym->binding == STB_LOCAL ? 'l' : 'w');
66 slsmg_write_nstring(sym->name, mb->namelen); 64 width = self->width - ((mb->addrlen * 2) + 4);
65 if (width > 0)
66 slsmg_write_nstring(sym->name, width);
67} 67}
68 68
69/* FIXME uber-kludgy, see comment on cmd_report... */ 69/* FIXME uber-kludgy, see comment on cmd_report... */
@@ -98,31 +98,29 @@ static int map_browser__search(struct map_browser *self)
98 return 0; 98 return 0;
99} 99}
100 100
101static int map_browser__run(struct map_browser *self, struct newtExitStruct *es) 101static int map_browser__run(struct map_browser *self)
102{ 102{
103 int key;
104
103 if (ui_browser__show(&self->b, self->map->dso->long_name, 105 if (ui_browser__show(&self->b, self->map->dso->long_name,
104 "Press <- or ESC to exit, %s / to search", 106 "Press <- or ESC to exit, %s / to search",
105 verbose ? "" : "restart with -v to use") < 0) 107 verbose ? "" : "restart with -v to use") < 0)
106 return -1; 108 return -1;
107 109
108 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
109 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
110 if (verbose) 110 if (verbose)
111 newtFormAddHotKey(self->b.form, '/'); 111 ui_browser__add_exit_key(&self->b, '/');
112 112
113 while (1) { 113 while (1) {
114 ui_browser__run(&self->b, es); 114 key = ui_browser__run(&self->b);
115 115
116 if (es->reason != NEWT_EXIT_HOTKEY) 116 if (verbose && key == '/')
117 break;
118 if (verbose && es->u.key == '/')
119 map_browser__search(self); 117 map_browser__search(self);
120 else 118 else
121 break; 119 break;
122 } 120 }
123 121
124 ui_browser__hide(&self->b); 122 ui_browser__hide(&self->b);
125 return 0; 123 return key;
126} 124}
127 125
128int map__browse(struct map *self) 126int map__browse(struct map *self)
@@ -136,7 +134,6 @@ int map__browse(struct map *self)
136 }, 134 },
137 .map = self, 135 .map = self,
138 }; 136 };
139 struct newtExitStruct es;
140 struct rb_node *nd; 137 struct rb_node *nd;
141 char tmp[BITS_PER_LONG / 4]; 138 char tmp[BITS_PER_LONG / 4];
142 u64 maxaddr = 0; 139 u64 maxaddr = 0;
@@ -144,8 +141,6 @@ int map__browse(struct map *self)
144 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) { 141 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
145 struct symbol *pos = rb_entry(nd, struct symbol, rb_node); 142 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
146 143
147 if (mb.namelen < pos->namelen)
148 mb.namelen = pos->namelen;
149 if (maxaddr < pos->end) 144 if (maxaddr < pos->end)
150 maxaddr = pos->end; 145 maxaddr = pos->end;
151 if (verbose) { 146 if (verbose) {
@@ -156,6 +151,5 @@ int map__browse(struct map *self)
156 } 151 }
157 152
158 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); 153 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
159 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen; 154 return map_browser__run(&mb);
160 return map_browser__run(&mb, &es);
161} 155}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
index 04600e26ceea..9706d9d40279 100644
--- a/tools/perf/util/ui/util.c
+++ b/tools/perf/util/ui/util.c
@@ -11,8 +11,6 @@
11#include "helpline.h" 11#include "helpline.h"
12#include "util.h" 12#include "util.h"
13 13
14newtComponent newt_form__new(void);
15
16static void newt_form__set_exit_keys(newtComponent self) 14static void newt_form__set_exit_keys(newtComponent self)
17{ 15{
18 newtFormAddHotKey(self, NEWT_KEY_LEFT); 16 newtFormAddHotKey(self, NEWT_KEY_LEFT);
@@ -22,7 +20,7 @@ static void newt_form__set_exit_keys(newtComponent self)
22 newtFormAddHotKey(self, CTRL('c')); 20 newtFormAddHotKey(self, CTRL('c'));
23} 21}
24 22
25newtComponent newt_form__new(void) 23static newtComponent newt_form__new(void)
26{ 24{
27 newtComponent self = newtForm(NULL, NULL, 0); 25 newtComponent self = newtForm(NULL, NULL, 0);
28 if (self) 26 if (self)
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