aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/probe-event.c
diff options
context:
space:
mode:
authorMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>2013-12-26 00:41:53 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2013-12-26 09:22:01 -0500
commitfb7345bbf7fad9bf72ef63a19c707970b9685812 (patch)
tree7e5dc7bb5ac68addf38718ee26ed1e91a36a60c6 /tools/perf/util/probe-event.c
parent8a613d40e389b723fd5889ac8d4033ed4030be31 (diff)
perf probe: Support basic dwarf-based operations on uprobe events
Support basic dwarf(debuginfo) based operations for uprobe events. With this change, perf probe can analyze debuginfo of user application binary to set up new uprobe event. This allows perf-probe --add(with local variables, line numbers) and --line works with -x option. (Actually, --vars has already accepted -x option) For example, the following command shows the probe-able lines of a given user space function. Something that so far was only available in the 'perf probe' tool for kernel space functions: # ./perf probe -x perf --line map__load <map__load@/home/fedora/ksrc/linux-2.6/tools/perf/util/map.c:0> 0 int map__load(struct map *map, symbol_filter_t filter) 1 { 2 const char *name = map->dso->long_name; int nr; 5 if (dso__loaded(map->dso, map->type)) 6 return 0; 8 nr = dso__load(map->dso, map, filter); 9 if (nr < 0) { 10 if (map->dso->has_build_id) { And this shows the available variables at the given line of the function. # ./perf probe -x perf --vars map__load:8 Available variables at map__load:8 @<map__load+96> char* name struct map* map symbol_filter_t filter @<map__find_symbol+112> char* name symbol_filter_t filter @<map__find_symbol_by_name+136> char* name symbol_filter_t filter @<map_groups__find_symbol_by_name+176> char* name struct map* map symbol_filter_t filter And lastly, we can now define probe(s) with all available variables on the given line: # ./perf probe -x perf --add 'map__load:8 $vars' Added new events: probe_perf:map__load (on map__load:8 with $vars) probe_perf:map__load_1 (on map__load:8 with $vars) probe_perf:map__load_2 (on map__load:8 with $vars) probe_perf:map__load_3 (on map__load:8 with $vars) You can now use it in all perf tools, such as: perf record -e probe_perf:map__load_3 -aR sleep 1 Changes from previous version: - Add examples in the patch description. - Use .text section start address and dwarf symbol address for calculating the offset of given symbol, instead of searching the symbol in symtab again. With this change, we can safely handle multiple local function instances (e.g. scnprintf in perf). Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: David Ahern <dsahern@gmail.com> Cc: David A. Long <dave.long@linaro.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: systemtap@sourceware.org Cc: yrl.pp-manager.tt@hitachi.com Link: http://lkml.kernel.org/r/20131226054152.22364.47021.stgit@kbuild-fedora.novalocal Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/probe-event.c')
-rw-r--r--tools/perf/util/probe-event.c151
1 files changed, 135 insertions, 16 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 68013b91377c..72b56aef105e 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -172,6 +172,52 @@ const char *kernel_get_module_path(const char *module)
172 return (dso) ? dso->long_name : NULL; 172 return (dso) ? dso->long_name : NULL;
173} 173}
174 174
175/* Copied from unwind.c */
176static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
177 GElf_Shdr *shp, const char *name)
178{
179 Elf_Scn *sec = NULL;
180
181 while ((sec = elf_nextscn(elf, sec)) != NULL) {
182 char *str;
183
184 gelf_getshdr(sec, shp);
185 str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
186 if (!strcmp(name, str))
187 break;
188 }
189
190 return sec;
191}
192
193static int get_text_start_address(const char *exec, unsigned long *address)
194{
195 Elf *elf;
196 GElf_Ehdr ehdr;
197 GElf_Shdr shdr;
198 int fd, ret = -ENOENT;
199
200 fd = open(exec, O_RDONLY);
201 if (fd < 0)
202 return -errno;
203
204 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
205 if (elf == NULL)
206 return -EINVAL;
207
208 if (gelf_getehdr(elf, &ehdr) == NULL)
209 goto out;
210
211 if (!elf_section_by_name(elf, &ehdr, &shdr, ".text"))
212 goto out;
213
214 *address = shdr.sh_addr - shdr.sh_offset;
215 ret = 0;
216out:
217 elf_end(elf);
218 return ret;
219}
220
175static int init_user_exec(void) 221static int init_user_exec(void)
176{ 222{
177 int ret = 0; 223 int ret = 0;
@@ -186,6 +232,37 @@ static int init_user_exec(void)
186 return ret; 232 return ret;
187} 233}
188 234
235static int convert_exec_to_group(const char *exec, char **result)
236{
237 char *ptr1, *ptr2, *exec_copy;
238 char buf[64];
239 int ret;
240
241 exec_copy = strdup(exec);
242 if (!exec_copy)
243 return -ENOMEM;
244
245 ptr1 = basename(exec_copy);
246 if (!ptr1) {
247 ret = -EINVAL;
248 goto out;
249 }
250
251 ptr2 = strpbrk(ptr1, "-._");
252 if (ptr2)
253 *ptr2 = '\0';
254 ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1);
255 if (ret < 0)
256 goto out;
257
258 *result = strdup(buf);
259 ret = *result ? 0 : -ENOMEM;
260
261out:
262 free(exec_copy);
263 return ret;
264}
265
189static int convert_to_perf_probe_point(struct probe_trace_point *tp, 266static int convert_to_perf_probe_point(struct probe_trace_point *tp,
190 struct perf_probe_point *pp) 267 struct perf_probe_point *pp)
191{ 268{
@@ -261,6 +338,40 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
261 return 0; 338 return 0;
262} 339}
263 340
341static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
342 int ntevs, const char *exec)
343{
344 int i, ret = 0;
345 unsigned long offset, stext = 0;
346 char buf[32];
347
348 if (!exec)
349 return 0;
350
351 ret = get_text_start_address(exec, &stext);
352 if (ret < 0)
353 return ret;
354
355 for (i = 0; i < ntevs && ret >= 0; i++) {
356 offset = tevs[i].point.address - stext;
357 offset += tevs[i].point.offset;
358 tevs[i].point.offset = 0;
359 free(tevs[i].point.symbol);
360 ret = e_snprintf(buf, 32, "0x%lx", offset);
361 if (ret < 0)
362 break;
363 tevs[i].point.module = strdup(exec);
364 tevs[i].point.symbol = strdup(buf);
365 if (!tevs[i].point.symbol || !tevs[i].point.module) {
366 ret = -ENOMEM;
367 break;
368 }
369 tevs[i].uprobes = true;
370 }
371
372 return ret;
373}
374
264static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, 375static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
265 int ntevs, const char *module) 376 int ntevs, const char *module)
266{ 377{
@@ -305,15 +416,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
305 struct debuginfo *dinfo; 416 struct debuginfo *dinfo;
306 int ntevs, ret = 0; 417 int ntevs, ret = 0;
307 418
308 if (pev->uprobes) {
309 if (need_dwarf) {
310 pr_warning("Debuginfo-analysis is not yet supported"
311 " with -x/--exec option.\n");
312 return -ENOSYS;
313 }
314 return convert_name_to_addr(pev, target);
315 }
316
317 dinfo = open_debuginfo(target); 419 dinfo = open_debuginfo(target);
318 420
319 if (!dinfo) { 421 if (!dinfo) {
@@ -332,9 +434,14 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
332 434
333 if (ntevs > 0) { /* Succeeded to find trace events */ 435 if (ntevs > 0) { /* Succeeded to find trace events */
334 pr_debug("find %d probe_trace_events.\n", ntevs); 436 pr_debug("find %d probe_trace_events.\n", ntevs);
335 if (target) 437 if (target) {
336 ret = add_module_to_probe_trace_events(*tevs, ntevs, 438 if (pev->uprobes)
337 target); 439 ret = add_exec_to_probe_trace_events(*tevs,
440 ntevs, target);
441 else
442 ret = add_module_to_probe_trace_events(*tevs,
443 ntevs, target);
444 }
338 return ret < 0 ? ret : ntevs; 445 return ret < 0 ? ret : ntevs;
339 } 446 }
340 447
@@ -654,9 +761,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
654 return -ENOSYS; 761 return -ENOSYS;
655 } 762 }
656 763
657 if (pev->uprobes)
658 return convert_name_to_addr(pev, target);
659
660 return 0; 764 return 0;
661} 765}
662 766
@@ -1913,14 +2017,29 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1913 int max_tevs, const char *target) 2017 int max_tevs, const char *target)
1914{ 2018{
1915 struct symbol *sym; 2019 struct symbol *sym;
1916 int ret = 0, i; 2020 int ret, i;
1917 struct probe_trace_event *tev; 2021 struct probe_trace_event *tev;
1918 2022
2023 if (pev->uprobes && !pev->group) {
2024 /* Replace group name if not given */
2025 ret = convert_exec_to_group(target, &pev->group);
2026 if (ret != 0) {
2027 pr_warning("Failed to make a group name.\n");
2028 return ret;
2029 }
2030 }
2031
1919 /* Convert perf_probe_event with debuginfo */ 2032 /* Convert perf_probe_event with debuginfo */
1920 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); 2033 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
1921 if (ret != 0) 2034 if (ret != 0)
1922 return ret; /* Found in debuginfo or got an error */ 2035 return ret; /* Found in debuginfo or got an error */
1923 2036
2037 if (pev->uprobes) {
2038 ret = convert_name_to_addr(pev, target);
2039 if (ret < 0)
2040 return ret;
2041 }
2042
1924 /* Allocate trace event buffer */ 2043 /* Allocate trace event buffer */
1925 tev = *tevs = zalloc(sizeof(struct probe_trace_event)); 2044 tev = *tevs = zalloc(sizeof(struct probe_trace_event));
1926 if (tev == NULL) 2045 if (tev == NULL)