|
@@ -1204,9 +1204,27 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
|
|
|
|
|
|
if (file_spec)
|
|
|
pp->file = tmp;
|
|
|
- else
|
|
|
+ else {
|
|
|
pp->function = tmp;
|
|
|
|
|
|
+ /*
|
|
|
+ * Keep pp->function even if this is absolute address,
|
|
|
+ * so it can mark whether abs_address is valid.
|
|
|
+ * Which make 'perf probe lib.bin 0x0' possible.
|
|
|
+ *
|
|
|
+ * Note that checking length of tmp is not needed
|
|
|
+ * because when we access tmp[1] we know tmp[0] is '0',
|
|
|
+ * so tmp[1] should always valid (but could be '\0').
|
|
|
+ */
|
|
|
+ if (tmp && !strncmp(tmp, "0x", 2)) {
|
|
|
+ pp->abs_address = strtoul(pp->function, &tmp, 0);
|
|
|
+ if (*tmp != '\0') {
|
|
|
+ semantic_error("Invalid absolute address.\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* Parse other options */
|
|
|
while (ptr) {
|
|
|
arg = ptr;
|
|
@@ -1804,14 +1822,29 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
|
|
|
if (len <= 0)
|
|
|
goto error;
|
|
|
|
|
|
- /* Uprobes must have tp->address and tp->module */
|
|
|
- if (tev->uprobes && (!tp->address || !tp->module))
|
|
|
+ /* Uprobes must have tp->module */
|
|
|
+ if (tev->uprobes && !tp->module)
|
|
|
goto error;
|
|
|
+ /*
|
|
|
+ * If tp->address == 0, then this point must be a
|
|
|
+ * absolute address uprobe.
|
|
|
+ * try_to_find_absolute_address() should have made
|
|
|
+ * tp->symbol to "0x0".
|
|
|
+ */
|
|
|
+ if (tev->uprobes && !tp->address) {
|
|
|
+ if (!tp->symbol || strcmp(tp->symbol, "0x0"))
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
|
|
|
/* Use the tp->address for uprobes */
|
|
|
if (tev->uprobes)
|
|
|
ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx",
|
|
|
tp->module, tp->address);
|
|
|
+ else if (!strncmp(tp->symbol, "0x", 2))
|
|
|
+ /* 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 ? ":" : "",
|
|
@@ -1874,8 +1907,8 @@ out:
|
|
|
}
|
|
|
|
|
|
static int convert_to_perf_probe_point(struct probe_trace_point *tp,
|
|
|
- struct perf_probe_point *pp,
|
|
|
- bool is_kprobe)
|
|
|
+ struct perf_probe_point *pp,
|
|
|
+ bool is_kprobe)
|
|
|
{
|
|
|
char buf[128];
|
|
|
int ret;
|
|
@@ -2331,7 +2364,9 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
|
|
|
if (pev->event)
|
|
|
event = pev->event;
|
|
|
else
|
|
|
- if (pev->point.function && !strisglob(pev->point.function))
|
|
|
+ if (pev->point.function &&
|
|
|
+ (strncmp(pev->point.function, "0x", 2) != 0) &&
|
|
|
+ !strisglob(pev->point.function))
|
|
|
event = pev->point.function;
|
|
|
else
|
|
|
event = tev->point.realname;
|
|
@@ -2598,6 +2633,98 @@ 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 (!(pev->point.function && !strncmp(pev->point.function, "0x", 2)))
|
|
|
+ return -EINVAL;
|
|
|
+ if (perf_probe_event_need_dwarf(pev))
|
|
|
+ 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.abs_address;
|
|
|
+ tp->retprobe = pp->retprobe;
|
|
|
+ tev->uprobes = pev->uprobes;
|
|
|
+
|
|
|
+ err = -ENOMEM;
|
|
|
+ /*
|
|
|
+ * Give it a '0x' leading symbol name.
|
|
|
+ * In __add_probe_trace_events, a NULL symbol is interpreted as
|
|
|
+ * invalud.
|
|
|
+ */
|
|
|
+ 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;
|
|
|
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
|
|
+ if (!tev->args) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+ 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,
|
|
@@ -2614,6 +2741,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)
|
|
@@ -2784,3 +2915,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;
|
|
|
+}
|