Skip to content

Commit a668281

Browse files
mhiramatrostedt
authored andcommitted
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@devbox Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
1 parent 59158ec commit a668281

File tree

3 files changed

+68
-10
lines changed

3 files changed

+68
-10
lines changed

kernel/trace/trace_kprobe.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ static bool within_notrace_func(struct trace_kprobe *tk)
366366
/* Internal register function - just handle k*probes and flags */
367367
static int __register_trace_kprobe(struct trace_kprobe *tk)
368368
{
369-
int ret;
369+
int i, ret;
370370

371371
if (trace_probe_is_registered(&tk->tp))
372372
return -EINVAL;
@@ -377,6 +377,12 @@ static int __register_trace_kprobe(struct trace_kprobe *tk)
377377
return -EINVAL;
378378
}
379379

380+
for (i = 0; i < tk->tp.nr_args; i++) {
381+
ret = traceprobe_update_arg(&tk->tp.args[i]);
382+
if (ret)
383+
return ret;
384+
}
385+
380386
/* Set/clear disabled flag according to tp->flag */
381387
if (trace_probe_is_enabled(&tk->tp))
382388
tk->rp.kp.flags &= ~KPROBE_FLAG_DISABLED;
@@ -928,6 +934,7 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
928934
{
929935
unsigned long val;
930936

937+
retry:
931938
/* 1st stage: get value from context */
932939
switch (code->op) {
933940
case FETCH_OP_REG:
@@ -953,6 +960,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
953960
val = regs_get_kernel_argument(regs, code->param);
954961
break;
955962
#endif
963+
case FETCH_NOP_SYMBOL: /* Ignore a place holder */
964+
code++;
965+
goto retry;
956966
default:
957967
return -EILSEQ;
958968
}

kernel/trace/trace_probe.c

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,16 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
251251
if (!(flags & TPARG_FL_KERNEL))
252252
return -EINVAL;
253253

254-
ret = traceprobe_split_symbol_offset(arg + 1, &offset);
255-
if (ret)
256-
break;
254+
/* Preserve symbol for updating */
255+
code->op = FETCH_NOP_SYMBOL;
256+
code->data = kstrdup(arg + 1, GFP_KERNEL);
257+
if (!code->data)
258+
return -ENOMEM;
259+
if (++code == end)
260+
return -E2BIG;
257261

258262
code->op = FETCH_OP_IMM;
259-
code->immediate =
260-
(unsigned long)kallsyms_lookup_name(arg + 1);
261-
if (!code->immediate)
262-
return -ENOENT;
263-
code->immediate += offset;
263+
code->immediate = 0;
264264
}
265265
/* These are fetching from memory */
266266
if (++code == end)
@@ -480,6 +480,11 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
480480
memcpy(parg->code, tmp, sizeof(*code) * (code - tmp + 1));
481481

482482
fail:
483+
if (ret) {
484+
for (code = tmp; code < tmp + FETCH_INSN_MAX; code++)
485+
if (code->op == FETCH_NOP_SYMBOL)
486+
kfree(code->data);
487+
}
483488
kfree(tmp);
484489

485490
return ret;
@@ -504,12 +509,53 @@ int traceprobe_conflict_field_name(const char *name,
504509

505510
void traceprobe_free_probe_arg(struct probe_arg *arg)
506511
{
512+
struct fetch_insn *code = arg->code;
513+
514+
while (code && code->op != FETCH_OP_END) {
515+
if (code->op == FETCH_NOP_SYMBOL)
516+
kfree(code->data);
517+
code++;
518+
}
507519
kfree(arg->code);
508520
kfree(arg->name);
509521
kfree(arg->comm);
510522
kfree(arg->fmt);
511523
}
512524

525+
int traceprobe_update_arg(struct probe_arg *arg)
526+
{
527+
struct fetch_insn *code = arg->code;
528+
long offset;
529+
char *tmp;
530+
char c;
531+
int ret = 0;
532+
533+
while (code && code->op != FETCH_OP_END) {
534+
if (code->op == FETCH_NOP_SYMBOL) {
535+
if (code[1].op != FETCH_OP_IMM)
536+
return -EINVAL;
537+
538+
tmp = strpbrk("+-", code->data);
539+
if (tmp)
540+
c = *tmp;
541+
ret = traceprobe_split_symbol_offset(code->data,
542+
&offset);
543+
if (ret)
544+
return ret;
545+
546+
code[1].immediate =
547+
(unsigned long)kallsyms_lookup_name(code->data);
548+
if (tmp)
549+
*tmp = c;
550+
if (!code[1].immediate)
551+
return -ENOENT;
552+
code[1].immediate += offset;
553+
}
554+
code++;
555+
}
556+
return 0;
557+
}
558+
513559
/* When len=0, we just calculate the needed length */
514560
#define LEN_OR_ZERO (len ? len - pos : 0)
515561
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,

kernel/trace/trace_probe.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ enum fetch_op {
100100
// Stage 5 (loop) op
101101
FETCH_OP_LP_ARRAY, /* Array: .param = loop count */
102102
FETCH_OP_END,
103+
FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */
103104
};
104105

105106
struct fetch_insn {
@@ -116,6 +117,7 @@ struct fetch_insn {
116117
unsigned char rshift;
117118
};
118119
unsigned long immediate;
120+
void *data;
119121
};
120122
};
121123

@@ -276,7 +278,7 @@ extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
276278
extern int traceprobe_conflict_field_name(const char *name,
277279
struct probe_arg *args, int narg);
278280

279-
extern void traceprobe_update_arg(struct probe_arg *arg);
281+
extern int traceprobe_update_arg(struct probe_arg *arg);
280282
extern void traceprobe_free_probe_arg(struct probe_arg *arg);
281283

282284
extern int traceprobe_split_symbol_offset(char *symbol, long *offset);

0 commit comments

Comments
 (0)