diff options
author | Wang Nan <wangnan0@huawei.com> | 2015-08-26 06:57:45 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-08-26 09:41:12 -0400 |
commit | da15bd9df4afd2f9f78cf29f85f013e3a38402b5 (patch) | |
tree | 131d0d4606401fbb722b3c6fe88ef349769cee32 /tools | |
parent | 6c6e024f0a62a6a08c06002fd3caa2307cc54fd0 (diff) |
perf probe: Support probing at absolute address
It should be useful to allow 'perf probe' probe at absolute offset of a
target. For example, when (u)probing at a instruction of a shared object
in a embedded system where debuginfo is not avaliable but we know the
offset of that instruction by manually digging.
This patch enables following perf probe command syntax:
# perf probe 0xffffffff811e6615
And
# perf probe /lib/x86_64-linux-gnu/libc-2.19.so 0xeb860
In the above example, we don't need a anchor symbol, so it is possible
to compute absolute addresses using other methods and then use 'perf
probe' to create the probing points.
v1 -> v2:
Drop the leading '+' in cmdline;
Allow uprobing at offset 0x0;
Improve 'perf probe -l' result when uprobe at area without debuginfo.
v2 -> v3:
Split bugfix to a separated patch.
Test result:
# perf probe 0xffffffff8119d175 %ax
# perf probe sys_write %ax
# perf probe /lib64/libc-2.18.so 0x0 %ax
# perf probe /lib64/libc-2.18.so 0x5 %ax
# perf probe /lib64/libc-2.18.so 0xd8e40 %ax
# perf probe /lib64/libc-2.18.so __write %ax
# perf probe /lib64/libc-2.18.so 0xd8e49 %ax
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_libc/abs_0 /lib64/libc-2.18.so:0x (null) arg1=%ax
p:probe_libc/abs_5 /lib64/libc-2.18.so:0x0000000000000005 arg1=%ax
p:probe_libc/abs_d8e40 /lib64/libc-2.18.so:0x00000000000d8e40 arg1=%ax
p:probe_libc/__write /lib64/libc-2.18.so:0x00000000000d8e40 arg1=%ax
p:probe_libc/abs_d8e49 /lib64/libc-2.18.so:0x00000000000d8e49 arg1=%ax
# cat /sys/kernel/debug/tracing/kprobe_events
p:probe/abs_ffffffff8119d175 0xffffffff8119d175 arg1=%ax
p:probe/sys_write _text+1692016 arg1=%ax
# perf probe -l
Failed to find debug information for address 5
probe:abs_ffffffff8119d175 (on sys_write+5 with arg1)
probe:sys_write (on sys_write with arg1)
probe_libc:__write (on @unix/syscall-template.S:81 in /lib64/libc-2.18.so with arg1)
probe_libc:abs_0 (on 0x0 in /lib64/libc-2.18.so with arg1)
probe_libc:abs_5 (on 0x5 in /lib64/libc-2.18.so with arg1)
probe_libc:abs_d8e40 (on @unix/syscall-template.S:81 in /lib64/libc-2.18.so with arg1)
probe_libc:abs_d8e49 (on __GI___libc_write+9 in /lib64/libc-2.18.so with arg1)
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1440586666-235233-7-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/util/probe-event.c | 162 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 4 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 21 |
3 files changed, 163 insertions, 24 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index eaacb58b9b36..eb5f18b75402 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -1204,9 +1204,27 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
1204 | 1204 | ||
1205 | if (file_spec) | 1205 | if (file_spec) |
1206 | pp->file = tmp; | 1206 | pp->file = tmp; |
1207 | else | 1207 | else { |
1208 | pp->function = tmp; | 1208 | pp->function = tmp; |
1209 | 1209 | ||
1210 | /* | ||
1211 | * Keep pp->function even if this is absolute address, | ||
1212 | * so it can mark whether abs_address is valid. | ||
1213 | * Which make 'perf probe lib.bin 0x0' possible. | ||
1214 | * | ||
1215 | * Note that checking length of tmp is not needed | ||
1216 | * because when we access tmp[1] we know tmp[0] is '0', | ||
1217 | * so tmp[1] should always valid (but could be '\0'). | ||
1218 | */ | ||
1219 | if (tmp && !strncmp(tmp, "0x", 2)) { | ||
1220 | pp->abs_address = strtoul(pp->function, &tmp, 0); | ||
1221 | if (*tmp != '\0') { | ||
1222 | semantic_error("Invalid absolute address.\n"); | ||
1223 | return -EINVAL; | ||
1224 | } | ||
1225 | } | ||
1226 | } | ||
1227 | |||
1210 | /* Parse other options */ | 1228 | /* Parse other options */ |
1211 | while (ptr) { | 1229 | while (ptr) { |
1212 | arg = ptr; | 1230 | arg = ptr; |
@@ -1804,14 +1822,29 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) | |||
1804 | if (len <= 0) | 1822 | if (len <= 0) |
1805 | goto error; | 1823 | goto error; |
1806 | 1824 | ||
1807 | /* Uprobes must have tp->address and tp->module */ | 1825 | /* Uprobes must have tp->module */ |
1808 | if (tev->uprobes && (!tp->address || !tp->module)) | 1826 | if (tev->uprobes && !tp->module) |
1809 | goto error; | 1827 | goto error; |
1828 | /* | ||
1829 | * If tp->address == 0, then this point must be a | ||
1830 | * absolute address uprobe. | ||
1831 | * try_to_find_absolute_address() should have made | ||
1832 | * tp->symbol to "0x0". | ||
1833 | */ | ||
1834 | if (tev->uprobes && !tp->address) { | ||
1835 | if (!tp->symbol || strcmp(tp->symbol, "0x0")) | ||
1836 | goto error; | ||
1837 | } | ||
1810 | 1838 | ||
1811 | /* Use the tp->address for uprobes */ | 1839 | /* Use the tp->address for uprobes */ |
1812 | if (tev->uprobes) | 1840 | if (tev->uprobes) |
1813 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", | 1841 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", |
1814 | tp->module, tp->address); | 1842 | tp->module, tp->address); |
1843 | else if (!strncmp(tp->symbol, "0x", 2)) | ||
1844 | /* Absolute address. See try_to_find_absolute_address() */ | ||
1845 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s0x%lx", | ||
1846 | tp->module ?: "", tp->module ? ":" : "", | ||
1847 | tp->address); | ||
1815 | else | 1848 | else |
1816 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", | 1849 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", |
1817 | tp->module ?: "", tp->module ? ":" : "", | 1850 | tp->module ?: "", tp->module ? ":" : "", |
@@ -1874,8 +1907,8 @@ out: | |||
1874 | } | 1907 | } |
1875 | 1908 | ||
1876 | static int convert_to_perf_probe_point(struct probe_trace_point *tp, | 1909 | static int convert_to_perf_probe_point(struct probe_trace_point *tp, |
1877 | struct perf_probe_point *pp, | 1910 | struct perf_probe_point *pp, |
1878 | bool is_kprobe) | 1911 | bool is_kprobe) |
1879 | { | 1912 | { |
1880 | char buf[128]; | 1913 | char buf[128]; |
1881 | int ret; | 1914 | int ret; |
@@ -2331,7 +2364,9 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev, | |||
2331 | if (pev->event) | 2364 | if (pev->event) |
2332 | event = pev->event; | 2365 | event = pev->event; |
2333 | else | 2366 | else |
2334 | if (pev->point.function && !strisglob(pev->point.function)) | 2367 | if (pev->point.function && |
2368 | (strncmp(pev->point.function, "0x", 2) != 0) && | ||
2369 | !strisglob(pev->point.function)) | ||
2335 | event = pev->point.function; | 2370 | event = pev->point.function; |
2336 | else | 2371 | else |
2337 | event = tev->point.realname; | 2372 | event = tev->point.realname; |
@@ -2598,6 +2633,98 @@ err_out: | |||
2598 | goto out; | 2633 | goto out; |
2599 | } | 2634 | } |
2600 | 2635 | ||
2636 | static int try_to_find_absolute_address(struct perf_probe_event *pev, | ||
2637 | struct probe_trace_event **tevs) | ||
2638 | { | ||
2639 | struct perf_probe_point *pp = &pev->point; | ||
2640 | struct probe_trace_event *tev; | ||
2641 | struct probe_trace_point *tp; | ||
2642 | int i, err; | ||
2643 | |||
2644 | if (!(pev->point.function && !strncmp(pev->point.function, "0x", 2))) | ||
2645 | return -EINVAL; | ||
2646 | if (perf_probe_event_need_dwarf(pev)) | ||
2647 | return -EINVAL; | ||
2648 | |||
2649 | /* | ||
2650 | * This is 'perf probe /lib/libc.so 0xabcd'. Try to probe at | ||
2651 | * absolute address. | ||
2652 | * | ||
2653 | * Only one tev can be generated by this. | ||
2654 | */ | ||
2655 | *tevs = zalloc(sizeof(*tev)); | ||
2656 | if (!*tevs) | ||
2657 | return -ENOMEM; | ||
2658 | |||
2659 | tev = *tevs; | ||
2660 | tp = &tev->point; | ||
2661 | |||
2662 | /* | ||
2663 | * Don't use tp->offset, use address directly, because | ||
2664 | * in synthesize_probe_trace_command() address cannot be | ||
2665 | * zero. | ||
2666 | */ | ||
2667 | tp->address = pev->point.abs_address; | ||
2668 | tp->retprobe = pp->retprobe; | ||
2669 | tev->uprobes = pev->uprobes; | ||
2670 | |||
2671 | err = -ENOMEM; | ||
2672 | /* | ||
2673 | * Give it a '0x' leading symbol name. | ||
2674 | * In __add_probe_trace_events, a NULL symbol is interpreted as | ||
2675 | * invalud. | ||
2676 | */ | ||
2677 | if (asprintf(&tp->symbol, "0x%lx", tp->address) < 0) | ||
2678 | goto errout; | ||
2679 | |||
2680 | /* For kprobe, check range */ | ||
2681 | if ((!tev->uprobes) && | ||
2682 | (kprobe_warn_out_range(tev->point.symbol, | ||
2683 | tev->point.address))) { | ||
2684 | err = -EACCES; | ||
2685 | goto errout; | ||
2686 | } | ||
2687 | |||
2688 | if (asprintf(&tp->realname, "abs_%lx", tp->address) < 0) | ||
2689 | goto errout; | ||
2690 | |||
2691 | if (pev->target) { | ||
2692 | tp->module = strdup(pev->target); | ||
2693 | if (!tp->module) | ||
2694 | goto errout; | ||
2695 | } | ||
2696 | |||
2697 | if (tev->group) { | ||
2698 | tev->group = strdup(pev->group); | ||
2699 | if (!tev->group) | ||
2700 | goto errout; | ||
2701 | } | ||
2702 | |||
2703 | if (pev->event) { | ||
2704 | tev->event = strdup(pev->event); | ||
2705 | if (!tev->event) | ||
2706 | goto errout; | ||
2707 | } | ||
2708 | |||
2709 | tev->nargs = pev->nargs; | ||
2710 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | ||
2711 | if (!tev->args) { | ||
2712 | err = -ENOMEM; | ||
2713 | goto errout; | ||
2714 | } | ||
2715 | for (i = 0; i < tev->nargs; i++) | ||
2716 | copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]); | ||
2717 | |||
2718 | return 1; | ||
2719 | |||
2720 | errout: | ||
2721 | if (*tevs) { | ||
2722 | clear_probe_trace_events(*tevs, 1); | ||
2723 | *tevs = NULL; | ||
2724 | } | ||
2725 | return err; | ||
2726 | } | ||
2727 | |||
2601 | bool __weak arch__prefers_symtab(void) { return false; } | 2728 | bool __weak arch__prefers_symtab(void) { return false; } |
2602 | 2729 | ||
2603 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 2730 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
@@ -2614,6 +2741,10 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
2614 | } | 2741 | } |
2615 | } | 2742 | } |
2616 | 2743 | ||
2744 | ret = try_to_find_absolute_address(pev, tevs); | ||
2745 | if (ret > 0) | ||
2746 | return ret; | ||
2747 | |||
2617 | if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { | 2748 | if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) { |
2618 | ret = find_probe_trace_events_from_map(pev, tevs); | 2749 | ret = find_probe_trace_events_from_map(pev, tevs); |
2619 | if (ret > 0) | 2750 | if (ret > 0) |
@@ -2784,3 +2915,22 @@ end: | |||
2784 | return ret; | 2915 | return ret; |
2785 | } | 2916 | } |
2786 | 2917 | ||
2918 | int copy_to_probe_trace_arg(struct probe_trace_arg *tvar, | ||
2919 | struct perf_probe_arg *pvar) | ||
2920 | { | ||
2921 | tvar->value = strdup(pvar->var); | ||
2922 | if (tvar->value == NULL) | ||
2923 | return -ENOMEM; | ||
2924 | if (pvar->type) { | ||
2925 | tvar->type = strdup(pvar->type); | ||
2926 | if (tvar->type == NULL) | ||
2927 | return -ENOMEM; | ||
2928 | } | ||
2929 | if (pvar->name) { | ||
2930 | tvar->name = strdup(pvar->name); | ||
2931 | if (tvar->name == NULL) | ||
2932 | return -ENOMEM; | ||
2933 | } else | ||
2934 | tvar->name = NULL; | ||
2935 | return 0; | ||
2936 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 83ee95e9743b..6e7ec68a4aa8 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -59,6 +59,7 @@ struct perf_probe_point { | |||
59 | bool retprobe; /* Return probe flag */ | 59 | bool retprobe; /* Return probe flag */ |
60 | char *lazy_line; /* Lazy matching pattern */ | 60 | char *lazy_line; /* Lazy matching pattern */ |
61 | unsigned long offset; /* Offset from function entry */ | 61 | unsigned long offset; /* Offset from function entry */ |
62 | unsigned long abs_address; /* Absolute address of the point */ | ||
62 | }; | 63 | }; |
63 | 64 | ||
64 | /* Perf probe probing argument field chain */ | 65 | /* Perf probe probing argument field chain */ |
@@ -156,4 +157,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) | |||
156 | /* Maximum index number of event-name postfix */ | 157 | /* Maximum index number of event-name postfix */ |
157 | #define MAX_EVENT_INDEX 1024 | 158 | #define MAX_EVENT_INDEX 1024 |
158 | 159 | ||
160 | int copy_to_probe_trace_arg(struct probe_trace_arg *tvar, | ||
161 | struct perf_probe_arg *pvar); | ||
162 | |||
159 | #endif /*_PROBE_EVENT_H */ | 163 | #endif /*_PROBE_EVENT_H */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 7b80f8cb62b9..29c43c0680a8 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -553,24 +553,9 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
553 | char buf[32], *ptr; | 553 | char buf[32], *ptr; |
554 | int ret = 0; | 554 | int ret = 0; |
555 | 555 | ||
556 | if (!is_c_varname(pf->pvar->var)) { | 556 | /* Copy raw parameters */ |
557 | /* Copy raw parameters */ | 557 | if (!is_c_varname(pf->pvar->var)) |
558 | pf->tvar->value = strdup(pf->pvar->var); | 558 | return copy_to_probe_trace_arg(pf->tvar, pf->pvar); |
559 | if (pf->tvar->value == NULL) | ||
560 | return -ENOMEM; | ||
561 | if (pf->pvar->type) { | ||
562 | pf->tvar->type = strdup(pf->pvar->type); | ||
563 | if (pf->tvar->type == NULL) | ||
564 | return -ENOMEM; | ||
565 | } | ||
566 | if (pf->pvar->name) { | ||
567 | pf->tvar->name = strdup(pf->pvar->name); | ||
568 | if (pf->tvar->name == NULL) | ||
569 | return -ENOMEM; | ||
570 | } else | ||
571 | pf->tvar->name = NULL; | ||
572 | return 0; | ||
573 | } | ||
574 | 559 | ||
575 | if (pf->pvar->name) | 560 | if (pf->pvar->name) |
576 | pf->tvar->name = strdup(pf->pvar->name); | 561 | pf->tvar->name = strdup(pf->pvar->name); |