Commit a6682814 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Steven Rostedt (VMware)

tracing/kprobes: Allow kprobe-events to record module symbol

Allow kprobe-events to record module symbols.

Since data symbols in a non-loaded module doesn't exist, it fails to
define such symbol as an argument of kprobe-event. But if the kprobe
event is defined on that module, we can defer to resolve the symbol
address.

Note that if given symbol is not found, the event is kept unavailable.
User can enable it but the event is not recorded.

Link: http://lkml.kernel.org/r/153547312336.26502.11432902826345374463.stgit@devboxSigned-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 59158ec4
...@@ -366,7 +366,7 @@ static bool within_notrace_func(struct trace_kprobe *tk) ...@@ -366,7 +366,7 @@ static bool within_notrace_func(struct trace_kprobe *tk)
/* Internal register function - just handle k*probes and flags */ /* Internal register function - just handle k*probes and flags */
static int __register_trace_kprobe(struct trace_kprobe *tk) static int __register_trace_kprobe(struct trace_kprobe *tk)
{ {
int ret; int i, ret;
if (trace_probe_is_registered(&tk->tp)) if (trace_probe_is_registered(&tk->tp))
return -EINVAL; return -EINVAL;
...@@ -377,6 +377,12 @@ static int __register_trace_kprobe(struct trace_kprobe *tk) ...@@ -377,6 +377,12 @@ static int __register_trace_kprobe(struct trace_kprobe *tk)
return -EINVAL; return -EINVAL;
} }
for (i = 0; i < tk->tp.nr_args; i++) {
ret = traceprobe_update_arg(&tk->tp.args[i]);
if (ret)
return ret;
}
/* Set/clear disabled flag according to tp->flag */ /* Set/clear disabled flag according to tp->flag */
if (trace_probe_is_enabled(&tk->tp)) if (trace_probe_is_enabled(&tk->tp))
tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED; tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED;
...@@ -928,6 +934,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, ...@@ -928,6 +934,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
{ {
unsigned long val; unsigned long val;
retry:
/* 1st stage: get value from context */ /* 1st stage: get value from context */
switch (code->op) { switch (code->op) {
case FETCH_OP_REG: case FETCH_OP_REG:
...@@ -953,6 +960,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, ...@@ -953,6 +960,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
val = regs_get_kernel_argument(regs, code->param); val = regs_get_kernel_argument(regs, code->param);
break; break;
#endif #endif
case FETCH_NOP_SYMBOL: /* Ignore a place holder */
code++;
goto retry;
default: default:
return -EILSEQ; return -EILSEQ;
} }
......
...@@ -251,16 +251,16 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ...@@ -251,16 +251,16 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
if (!(flags & TPARG_FL_KERNEL)) if (!(flags & TPARG_FL_KERNEL))
return -EINVAL; return -EINVAL;
ret = traceprobe_split_symbol_offset(arg + 1, &offset); /* Preserve symbol for updating */
if (ret) code->op = FETCH_NOP_SYMBOL;
break; code->data = kstrdup(arg + 1, GFP_KERNEL);
if (!code->data)
return -ENOMEM;
if (++code == end)
return -E2BIG;
code->op = FETCH_OP_IMM; code->op = FETCH_OP_IMM;
code->immediate = code->immediate = 0;
(unsigned long)kallsyms_lookup_name(arg + 1);
if (!code->immediate)
return -ENOENT;
code->immediate += offset;
} }
/* These are fetching from memory */ /* These are fetching from memory */
if (++code == end) if (++code == end)
...@@ -480,6 +480,11 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, ...@@ -480,6 +480,11 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1)); memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1));
fail: fail:
if (ret) {
for (code = tmp; code < tmp + FETCH_INSN_MAX; code++)
if (code->op == FETCH_NOP_SYMBOL)
kfree(code->data);
}
kfree(tmp); kfree(tmp);
return ret; return ret;
...@@ -504,12 +509,53 @@ int traceprobe_conflict_field_name(const char *name, ...@@ -504,12 +509,53 @@ int traceprobe_conflict_field_name(const char *name,
void traceprobe_free_probe_arg(struct probe_arg *arg) void traceprobe_free_probe_arg(struct probe_arg *arg)
{ {
struct fetch_insn *code = arg->code;
while (code && code->op != FETCH_OP_END) {
if (code->op == FETCH_NOP_SYMBOL)
kfree(code->data);
code++;
}
kfree(arg->code); kfree(arg->code);
kfree(arg->name); kfree(arg->name);
kfree(arg->comm); kfree(arg->comm);
kfree(arg->fmt); kfree(arg->fmt);
} }
int traceprobe_update_arg(struct probe_arg *arg)
{
struct fetch_insn *code = arg->code;
long offset;
char *tmp;
char c;
int ret = 0;
while (code && code->op != FETCH_OP_END) {
if (code->op == FETCH_NOP_SYMBOL) {
if (code[1].op != FETCH_OP_IMM)
return -EINVAL;
tmp = strpbrk("+-", code->data);
if (tmp)
c = *tmp;
ret = traceprobe_split_symbol_offset(code->data,
&offset);
if (ret)
return ret;
code[1].immediate =
(unsigned long)kallsyms_lookup_name(code->data);
if (tmp)
*tmp = c;
if (!code[1].immediate)
return -ENOENT;
code[1].immediate += offset;
}
code++;
}
return 0;
}
/* When len=0, we just calculate the needed length */ /* When len=0, we just calculate the needed length */
#define LEN_OR_ZERO (len ? len - pos : 0) #define LEN_OR_ZERO (len ? len - pos : 0)
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
......
...@@ -100,6 +100,7 @@ enum fetch_op { ...@@ -100,6 +100,7 @@ enum fetch_op {
// Stage 5 (loop) op // Stage 5 (loop) op
FETCH_OP_LP_ARRAY, /* Array: .param = loop count */ FETCH_OP_LP_ARRAY, /* Array: .param = loop count */
FETCH_OP_END, FETCH_OP_END,
FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */
}; };
struct fetch_insn { struct fetch_insn {
...@@ -116,6 +117,7 @@ struct fetch_insn { ...@@ -116,6 +117,7 @@ struct fetch_insn {
unsigned char rshift; unsigned char rshift;
}; };
unsigned long immediate; unsigned long immediate;
void *data;
}; };
}; };
...@@ -276,7 +278,7 @@ extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size, ...@@ -276,7 +278,7 @@ extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
extern int traceprobe_conflict_field_name(const char *name, extern int traceprobe_conflict_field_name(const char *name,
struct probe_arg *args, int narg); struct probe_arg *args, int narg);
extern void traceprobe_update_arg(struct probe_arg *arg); extern int traceprobe_update_arg(struct probe_arg *arg);
extern void traceprobe_free_probe_arg(struct probe_arg *arg); extern void traceprobe_free_probe_arg(struct probe_arg *arg);
extern int traceprobe_split_symbol_offset(char *symbol, long *offset); extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment