Em Wed, Aug 26, 2015 at 10:38:18AM +0800, Wangnan (F) escreveu:
On 2015/8/26 8:02, åæéå / HIRAMATUïMASAMI wrote:Agreed, I also think that using '+' is better, but will not dwell on
I will change 2/2 as you suggection.From: Wang Nan [mailto:wangnan0@xxxxxxxxxx]Why do we need "+" for the 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
It seems that we can do it if we find that the given probe point
starts with "0x".
Thanks,
However, we can only ensure that in kernel side symbol never leading
with '0x'. Although I don't think symbol leading with 0x is useful,
it is still possible for a userspace program compiled and linked by
a language other than C produces such symbol. '+' helps us separate
address and function name semantically, make us don't rely on assumption
on function names. If in future we do meet '0x' symbols, I think we still
need the '+' syntax back. But we can do it at that time.
this so as to make progress :-)
- Arnaldo
Thank you.
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.
Signed-off-by: Wang Nan <wangnan0@xxxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>
Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
---
tools/perf/util/probe-event.c | 144 +++++++++++++++++++++++++++++++++++++----
tools/perf/util/probe-event.h | 3 +
tools/perf/util/probe-finder.c | 21 +-----
3 files changed, 138 insertions(+), 30 deletions(-)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6c7e538..59de69a4 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1194,9 +1194,13 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
*ptr++ = '\0';
}
- tmp = strdup(arg);
- if (tmp == NULL)
- return -ENOMEM;
+ if (arg[0] == '\0')
+ tmp = NULL;
+ else {
+ tmp = strdup(arg);
+ if (tmp == NULL)
+ return -ENOMEM;
+ }
if (file_spec)
pp->file = tmp;
@@ -1283,11 +1287,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
return -EINVAL;
}
- if (pp->offset && !pp->function) {
- semantic_error("Offset requires an entry function.\n");
- return -EINVAL;
- }
-
if (pp->retprobe && !pp->function) {
semantic_error("Return probe requires an entry function.\n");
return -EINVAL;
@@ -1299,6 +1298,11 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
return -EINVAL;
}
+ if (!pp->function && !pp->offset && !pp->file) {
+ semantic_error("Absolute address should not be zero.\n");
+ return -EINVAL;
+ }
+
pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
pp->lazy_line);
@@ -1609,7 +1613,7 @@ error:
static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
{
char *buf, *tmp;
- char offs[32] = "", line[32] = "", file[32] = "";
+ char offs[32] = "", line[32] = "", file[32] = "", addr[32] = "";
int ret, len;
buf = zalloc(MAX_CMDLEN);
@@ -1622,6 +1626,11 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
if (ret <= 0)
goto error;
}
+ if (!pp->function) {
+ ret = e_snprintf(addr, 32, "0x%lx", pp->offset);
+ if (ret <= 0)
+ goto error;
+ }
if (pp->line) {
ret = e_snprintf(line, 32, ":%d", pp->line);
if (ret <= 0)
@@ -1639,9 +1648,11 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
goto error;
}
- if (pp->function)
- ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function,
- offs, pp->retprobe ? "%return" : "", line,
+ if (pp->function || pp->offset)
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s",
+ pp->function ? : addr,
+ pp->function ? offs : "",
+ pp->retprobe ? "%return" : "", line,
file);
else
ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line);
@@ -1786,6 +1797,11 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
if (tev->uprobes)
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
tp->module, tp->address);
+ else if (tp->symbol[0] == '0' && tp->symbol[1] == 'x')
+ /* Absolute address. See try_to_find_absolute_address() */
+ ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s0x%lx",
+ tp->module ?: "", tp->module ? ":" : "",
+ tp->address);
else
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu",
tp->module ?: "", tp->module ? ":" : "",
@@ -2572,6 +2588,87 @@ err_out:
goto out;
}
+static int try_to_find_absolute_address(struct perf_probe_event *pev,
+ struct probe_trace_event **tevs)
+{
+ struct perf_probe_point *pp = &pev->point;
+ struct probe_trace_event *tev;
+ struct probe_trace_point *tp;
+ int i, err;
+
+ if (perf_probe_event_need_dwarf(pev) || pev->point.function)
+ return -EINVAL;
+
+ /*
+ * This is 'perf probe /lib/libc.so +0xabcd'. Try to probe at
+ * absolute address.
+ *
+ * Only one tev can be generated by this.
+ */
+ *tevs = zalloc(sizeof(*tev));
+ if (!*tevs)
+ return -ENOMEM;
+
+ tev = *tevs;
+ tp = &tev->point;
+
+ /*
+ * Don't use tp->offset, use address directly, because
+ * in synthesize_probe_trace_command() address cannot be
+ * zero.
+ */
+ tp->address = pev->point.offset;
+ tp->retprobe = pp->retprobe;
+ tev->uprobes = pev->uprobes;
+
+ err = -ENOMEM;
+ /* Give it a '0x' leading symbol name */
+ if (asprintf(&tp->symbol, "0x%lx", tp->address) < 0)
+ goto errout;
+
+ /* For kprobe, check range */
+ if ((!tev->uprobes) &&
+ (kprobe_warn_out_range(tev->point.symbol,
+ tev->point.address))) {
+ err = -EACCES;
+ goto errout;
+ }
+
+ if (asprintf(&tp->realname, "abs_%lx", tp->address) < 0)
+ goto errout;
+
+ if (pev->target) {
+ tp->module = strdup(pev->target);
+ if (!tp->module)
+ goto errout;
+ }
+
+ if (tev->group) {
+ tev->group = strdup(pev->group);
+ if (!tev->group)
+ goto errout;
+ }
+
+ if (pev->event) {
+ tev->event = strdup(pev->event);
+ if (!tev->event)
+ goto errout;
+ }
+
+ tev->nargs = pev->nargs;
+ for (i = 0; i < tev->nargs; i++)
+ copy_to_probe_trace_arg(&tev->args[i], &pev->args[i]);
+
+ return 1;
+
+errout:
+ if (*tevs) {
+ clear_probe_trace_events(*tevs, 1);
+ *tevs = NULL;
+ }
+ return err;
+}
+
bool __weak arch__prefers_symtab(void) { return false; }
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
@@ -2588,6 +2685,10 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
}
}
+ ret = try_to_find_absolute_address(pev, tevs);
+ if (ret > 0)
+ return ret;
+
if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
ret = find_probe_trace_events_from_map(pev, tevs);
if (ret > 0)
@@ -2758,3 +2859,22 @@ end:
return ret;
}
+int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
+ struct perf_probe_arg *pvar)
+{
+ tvar->value = strdup(pvar->var);
+ if (tvar->value == NULL)
+ return -ENOMEM;
+ if (pvar->type) {
+ tvar->type = strdup(pvar->type);
+ if (tvar->type == NULL)
+ return -ENOMEM;
+ }
+ if (pvar->name) {
+ tvar->name = strdup(pvar->name);
+ if (tvar->name == NULL)
+ return -ENOMEM;
+ } else
+ tvar->name = NULL;
+ return 0;
+}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 83ee95e..174a3cf 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -156,4 +156,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...)
/* Maximum index number of event-name postfix */
#define MAX_EVENT_INDEX 1024
+int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
+ struct perf_probe_arg *pvar);
+
#endif /*_PROBE_EVENT_H */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 7b80f8c..29c43c068 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)
char buf[32], *ptr;
int ret = 0;
- if (!is_c_varname(pf->pvar->var)) {
- /* Copy raw parameters */
- pf->tvar->value = strdup(pf->pvar->var);
- if (pf->tvar->value == NULL)
- return -ENOMEM;
- if (pf->pvar->type) {
- pf->tvar->type = strdup(pf->pvar->type);
- if (pf->tvar->type == NULL)
- return -ENOMEM;
- }
- if (pf->pvar->name) {
- pf->tvar->name = strdup(pf->pvar->name);
- if (pf->tvar->name == NULL)
- return -ENOMEM;
- } else
- pf->tvar->name = NULL;
- return 0;
- }
+ /* Copy raw parameters */
+ if (!is_c_varname(pf->pvar->var))
+ return copy_to_probe_trace_arg(pf->tvar, pf->pvar);
if (pf->pvar->name)
pf->tvar->name = strdup(pf->pvar->name);
--
1.8.3.4