Re: [PATCH -tip 2/3] perf-probe: Support dwarf on uprobe events

From: Masami Hiramatsu
Date: Sun Dec 22 2013 - 16:39:30 EST


(2013/12/21 3:01), Arnaldo Carvalho de Melo wrote:
> Em Fri, Dec 20, 2013 at 10:02:59AM +0000, Masami Hiramatsu escreveu:
>> Support 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/--line works with -x option.
>> (Actually, --vars has already accepted -x option)
>
> Can you provide an example?
>

OK, here is the examples from 0/3. :)
Or, should I better put this into the patch description too?

For example)
- Shows the probe-able lines of the given function
----
# ./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) {
----

- Shows the available variables of the given line
----
# ./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
----

- Set a probe with available vars 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
----

> Showing output from commands when new features are implemented can speed
> up the process of having it used :-)
>
> - Arnaldo
>
>> Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>
>> ---
>> tools/perf/builtin-probe.c | 2
>> tools/perf/util/probe-event.c | 230 +++++++++++++++++++++++++++--------------
>> 2 files changed, 155 insertions(+), 77 deletions(-)
>>
>> diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
>> index b40d064..7fc566a 100644
>> --- a/tools/perf/builtin-probe.c
>> +++ b/tools/perf/builtin-probe.c
>> @@ -420,7 +420,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
>> }
>>
>> #ifdef HAVE_DWARF_SUPPORT
>> - if (params.show_lines && !params.uprobes) {
>> + if (params.show_lines) {
>> if (params.mod_events) {
>> pr_err(" Error: Don't use --line with"
>> " --add/--del.\n");
>> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
>> index 05be5de..e27cecb 100644
>> --- a/tools/perf/util/probe-event.c
>> +++ b/tools/perf/util/probe-event.c
>> @@ -186,6 +186,90 @@ static int init_user_exec(void)
>> return ret;
>> }
>>
>> +static const char *__target_symbol;
>> +static struct symbol *__result_sym;
>> +
>> +static int filter_target_symbol(struct map *map __maybe_unused,
>> + struct symbol *sym)
>> +{
>> + if (strcmp(__target_symbol, sym->name) == 0) {
>> + __result_sym = sym;
>> + return 0;
>> + }
>> + return 1;
>> +}
>> +
>> +/* Find the offset of the symbol in the executable binary */
>> +static int find_symbol_offset(const char *exec, const char *function,
>> + unsigned long *offs)
>> +{
>> + struct symbol *sym;
>> + struct map *map = NULL;
>> + int ret = -EINVAL;
>> +
>> + if (!offs)
>> + return -EINVAL;
>> +
>> + map = dso__new_map(exec);
>> + if (!map) {
>> + pr_warning("Cannot find appropriate DSO for %s.\n", exec);
>> + goto out;
>> + }
>> + pr_debug("Search %s in %s\n", function, exec);
>> + __target_symbol = function;
>> + __result_sym = NULL;
>> + if (map__load(map, filter_target_symbol)) {
>> + pr_err("Failed to find %s in %s.\n", function, exec);
>> + goto out;
>> + }
>> + sym = __result_sym;
>> + if (!sym) {
>> + pr_warning("Cannot find %s in DSO %s\n", function, exec);
>> + goto out;
>> + }
>> +
>> + *offs = (map->start > sym->start) ? map->start : 0;
>> + *offs += sym->start + map->pgoff;
>> + ret = 0;
>> +out:
>> + if (map) {
>> + dso__delete(map->dso);
>> + map__delete(map);
>> + }
>> + return ret;
>> +}
>> +
>> +static int convert_exec_to_group(const char *exec, char **result)
>> +{
>> + char *ptr1, *ptr2, *exec_copy;
>> + char buf[64];
>> + int ret;
>> +
>> + exec_copy = strdup(exec);
>> + if (!exec_copy)
>> + return -ENOMEM;
>> +
>> + ptr1 = basename(exec_copy);
>> + if (!ptr1) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + ptr2 = strpbrk(ptr1, "-._");
>> + if (ptr2)
>> + *ptr2 = '\0';
>> + ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1);
>> + if (ret < 0)
>> + goto out;
>> +
>> + *result = strdup(buf);
>> + ret = *result ? 0 : -ENOMEM;
>> +
>> +out:
>> + free(exec_copy);
>> + return ret;
>> +}
>> +
>> static int convert_to_perf_probe_point(struct probe_trace_point *tp,
>> struct perf_probe_point *pp)
>> {
>> @@ -261,6 +345,45 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
>> return 0;
>> }
>>
>> +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
>> + int ntevs, const char *exec,
>> + const char *group)
>> +{
>> + int i, ret = 0;
>> + unsigned long offset;
>> + char buf[32];
>> +
>> + if (!exec)
>> + return 0;
>> +
>> + for (i = 0; i < ntevs && ret >= 0; i++) {
>> + /* Get proper offset */
>> + ret = find_symbol_offset(exec, tevs[i].point.symbol, &offset);
>> + if (ret < 0)
>> + break;
>> + offset += tevs[i].point.offset;
>> + tevs[i].point.offset = 0;
>> + free(tevs[i].point.symbol);
>> + ret = e_snprintf(buf, 32, "0x%lx", offset);
>> + if (ret < 0)
>> + break;
>> + tevs[i].point.module = strdup(exec);
>> + tevs[i].point.symbol = strdup(buf);
>> + if (!tevs[i].point.symbol || !tevs[i].point.module) {
>> + ret = -ENOMEM;
>> + break;
>> + }
>> + /* Replace group name if not given */
>> + if (!group) {
>> + free(tevs[i].group);
>> + ret = convert_exec_to_group(exec, &tevs[i].group);
>> + }
>> + tevs[i].uprobes = true;
>> + }
>> +
>> + return ret;
>> +}
>> +
>> static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
>> int ntevs, const char *module)
>> {
>> @@ -305,15 +428,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
>> struct debuginfo *dinfo;
>> int ntevs, ret = 0;
>>
>> - if (pev->uprobes) {
>> - if (need_dwarf) {
>> - pr_warning("Debuginfo-analysis is not yet supported"
>> - " with -x/--exec option.\n");
>> - return -ENOSYS;
>> - }
>> - return convert_name_to_addr(pev, target);
>> - }
>> -
>> dinfo = open_debuginfo(target);
>>
>> if (!dinfo) {
>> @@ -332,9 +446,14 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
>>
>> if (ntevs > 0) { /* Succeeded to find trace events */
>> pr_debug("find %d probe_trace_events.\n", ntevs);
>> - if (target)
>> - ret = add_module_to_probe_trace_events(*tevs, ntevs,
>> - target);
>> + if (target) {
>> + if (pev->uprobes)
>> + ret = add_exec_to_probe_trace_events(*tevs,
>> + ntevs, target, pev->group);
>> + else
>> + ret = add_module_to_probe_trace_events(*tevs,
>> + ntevs, target);
>> + }
>> return ret < 0 ? ret : ntevs;
>> }
>>
>> @@ -654,9 +773,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
>> return -ENOSYS;
>> }
>>
>> - if (pev->uprobes)
>> - return convert_name_to_addr(pev, target);
>> -
>> return 0;
>> }
>>
>> @@ -1916,11 +2032,26 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
>> int ret = 0, i;
>> struct probe_trace_event *tev;
>>
>> + if (pev->uprobes)
>> + if (!pev->group) {
>> + ret = convert_exec_to_group(target, &pev->group);
>> + if (ret != 0) {
>> + pr_warning("Failed to make group name.\n");
>> + return ret;
>> + }
>> + }
>> +
>> /* Convert perf_probe_event with debuginfo */
>> ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target);
>> if (ret != 0)
>> return ret; /* Found in debuginfo or got an error */
>>
>> + if (pev->uprobes) {
>> + ret = convert_name_to_addr(pev, target);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> /* Allocate trace event buffer */
>> tev = *tevs = zalloc(sizeof(struct probe_trace_event));
>> if (tev == NULL)
>> @@ -2279,88 +2410,35 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
>> static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
>> {
>> struct perf_probe_point *pp = &pev->point;
>> - struct symbol *sym;
>> - struct map *map = NULL;
>> - char *function = NULL;
>> int ret = -EINVAL;
>> - unsigned long long vaddr = 0;
>> + unsigned long vaddr = 0;
>>
>> if (!pp->function) {
>> pr_warning("No function specified for uprobes");
>> goto out;
>> }
>>
>> - function = strdup(pp->function);
>> - if (!function) {
>> - pr_warning("Failed to allocate memory by strdup.\n");
>> - ret = -ENOMEM;
>> - goto out;
>> - }
>> -
>> - map = dso__new_map(exec);
>> - if (!map) {
>> - pr_warning("Cannot find appropriate DSO for %s.\n", exec);
>> - goto out;
>> - }
>> - available_func_filter = strfilter__new(function, NULL);
>> - if (map__load(map, filter_available_functions)) {
>> - pr_err("Failed to load map.\n");
>> - goto out;
>> - }
>> -
>> - sym = map__find_symbol_by_name(map, function, NULL);
>> - if (!sym) {
>> - pr_warning("Cannot find %s in DSO %s\n", function, exec);
>> + ret = find_symbol_offset(exec, pp->function, &vaddr);
>> + if (ret < 0)
>> goto out;
>> - }
>>
>> - if (map->start > sym->start)
>> - vaddr = map->start;
>> - vaddr += sym->start + pp->offset + map->pgoff;
>> + vaddr += pp->offset;
>> pp->offset = 0;
>>
>> if (!pev->event) {
>> - pev->event = function;
>> - function = NULL;
>> - }
>> - if (!pev->group) {
>> - char *ptr1, *ptr2, *exec_copy;
>> -
>> - pev->group = zalloc(sizeof(char *) * 64);
>> - exec_copy = strdup(exec);
>> - if (!exec_copy) {
>> - ret = -ENOMEM;
>> - pr_warning("Failed to copy exec string.\n");
>> - goto out;
>> - }
>> + pev->event = pp->function;
>> + } else
>> + free(pp->function);
>>
>> - ptr1 = strdup(basename(exec_copy));
>> - if (ptr1) {
>> - ptr2 = strpbrk(ptr1, "-._");
>> - if (ptr2)
>> - *ptr2 = '\0';
>> - e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP,
>> - ptr1);
>> - free(ptr1);
>> - }
>> - free(exec_copy);
>> - }
>> - free(pp->function);
>> pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
>> if (!pp->function) {
>> ret = -ENOMEM;
>> pr_warning("Failed to allocate memory by zalloc.\n");
>> goto out;
>> }
>> - e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
>> + e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%lx", vaddr);
>> ret = 0;
>>
>> out:
>> - if (map) {
>> - dso__delete(map->dso);
>> - map__delete(map);
>> - }
>> - if (function)
>> - free(function);
>> return ret;
>> }
>>
>


--
Masami HIRAMATSU
IT Management Research Dept. Linux Technology Center
Hitachi, Ltd., Yokohama Research Laboratory
E-mail: masami.hiramatsu.pt@xxxxxxxxxxx


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/