tracing/probes: Support $argN in return probe (kprobe and fprobe)

Support accessing $argN in the return probe events. This will help users to
record entry data in function return (exit) event for simplfing the function
entry/exit information in one event, and record the result values (e.g.
allocated object/initialized object) at function exit.

For example, if we have a function `int init_foo(struct foo *obj, int param)`
sometimes we want to check how `obj` is initialized. In such case, we can
define a new return event like below;

 # echo 'r init_foo retval=$retval param=$arg2 field1=+0($arg1)' >> kprobe_events

Thus it records the function parameter `param` and its result `obj->field1`
(the dereference will be done in the function exit timing) value at once.

This also support fprobe, BTF args and'$arg*'. So if CONFIG_DEBUG_INFO_BTF
is enabled, we can trace both function parameters and the return value
by following command.

 # echo 'f target_function%return $arg* $retval' >> dynamic_events

Link: https://lore.kernel.org/all/170952365552.229804.224112990211602895.stgit@devnote2/Signed-off-by: default avatarMasami Hiramatsu (Google) <mhiramat@kernel.org>
parent c18f9eab
...@@ -5755,6 +5755,7 @@ static const char readme_msg[] = ...@@ -5755,6 +5755,7 @@ static const char readme_msg[] =
"\t $stack<index>, $stack, $retval, $comm,\n" "\t $stack<index>, $stack, $retval, $comm,\n"
#endif #endif
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n" "\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
"\t kernel return probes support: $retval, $arg<N>, $comm\n"
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n" "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, char, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n" "\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
"\t symstr, <type>\\[<array-size>\\]\n" "\t symstr, <type>\\[<array-size>\\]\n"
......
...@@ -390,8 +390,8 @@ static int get_eprobe_size(struct trace_probe *tp, void *rec) ...@@ -390,8 +390,8 @@ static int get_eprobe_size(struct trace_probe *tp, void *rec)
/* Note that we don't verify it, since the code does not come from user space */ /* Note that we don't verify it, since the code does not come from user space */
static int static int
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
void *base) void *dest, void *base)
{ {
unsigned long val; unsigned long val;
int ret; int ret;
...@@ -438,7 +438,7 @@ __eprobe_trace_func(struct eprobe_data *edata, void *rec) ...@@ -438,7 +438,7 @@ __eprobe_trace_func(struct eprobe_data *edata, void *rec)
return; return;
entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event); entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
store_trace_args(&entry[1], &edata->ep->tp, rec, sizeof(*entry), dsize); store_trace_args(&entry[1], &edata->ep->tp, rec, NULL, sizeof(*entry), dsize);
trace_event_buffer_commit(&fbuffer); trace_event_buffer_commit(&fbuffer);
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright (C) 2022 Google LLC. * Copyright (C) 2022 Google LLC.
*/ */
#define pr_fmt(fmt) "trace_fprobe: " fmt #define pr_fmt(fmt) "trace_fprobe: " fmt
#include <asm/ptrace.h>
#include <linux/fprobe.h> #include <linux/fprobe.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -129,8 +130,8 @@ static bool trace_fprobe_is_registered(struct trace_fprobe *tf) ...@@ -129,8 +130,8 @@ static bool trace_fprobe_is_registered(struct trace_fprobe *tf)
* from user space. * from user space.
*/ */
static int static int
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
void *base) void *dest, void *base)
{ {
struct pt_regs *regs = rec; struct pt_regs *regs = rec;
unsigned long val; unsigned long val;
...@@ -152,6 +153,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -152,6 +153,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
case FETCH_OP_ARG: case FETCH_OP_ARG:
val = regs_get_kernel_argument(regs, code->param); val = regs_get_kernel_argument(regs, code->param);
break; break;
case FETCH_OP_EDATA:
val = *(unsigned long *)((unsigned long)edata + code->offset);
break;
#endif #endif
case FETCH_NOP_SYMBOL: /* Ignore a place holder */ case FETCH_NOP_SYMBOL: /* Ignore a place holder */
code++; code++;
...@@ -184,7 +188,7 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -184,7 +188,7 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
if (trace_trigger_soft_disabled(trace_file)) if (trace_trigger_soft_disabled(trace_file))
return; return;
dsize = __get_data_size(&tf->tp, regs); dsize = __get_data_size(&tf->tp, regs, NULL);
entry = trace_event_buffer_reserve(&fbuffer, trace_file, entry = trace_event_buffer_reserve(&fbuffer, trace_file,
sizeof(*entry) + tf->tp.size + dsize); sizeof(*entry) + tf->tp.size + dsize);
...@@ -194,7 +198,7 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -194,7 +198,7 @@ __fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
fbuffer.regs = regs; fbuffer.regs = regs;
entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event); entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
entry->ip = entry_ip; entry->ip = entry_ip;
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tf->tp, regs, NULL, sizeof(*entry), dsize);
trace_event_buffer_commit(&fbuffer); trace_event_buffer_commit(&fbuffer);
} }
...@@ -211,10 +215,23 @@ fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -211,10 +215,23 @@ fentry_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
NOKPROBE_SYMBOL(fentry_trace_func); NOKPROBE_SYMBOL(fentry_trace_func);
/* function exit handler */ /* function exit handler */
static int trace_fprobe_entry_handler(struct fprobe *fp, unsigned long entry_ip,
unsigned long ret_ip, struct pt_regs *regs,
void *entry_data)
{
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
if (tf->tp.entry_arg)
store_trace_entry_data(entry_data, &tf->tp, regs);
return 0;
}
NOKPROBE_SYMBOL(trace_fprobe_entry_handler)
static nokprobe_inline void static nokprobe_inline void
__fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
unsigned long ret_ip, struct pt_regs *regs, unsigned long ret_ip, struct pt_regs *regs,
struct trace_event_file *trace_file) void *entry_data, struct trace_event_file *trace_file)
{ {
struct fexit_trace_entry_head *entry; struct fexit_trace_entry_head *entry;
struct trace_event_buffer fbuffer; struct trace_event_buffer fbuffer;
...@@ -227,7 +244,7 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -227,7 +244,7 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
if (trace_trigger_soft_disabled(trace_file)) if (trace_trigger_soft_disabled(trace_file))
return; return;
dsize = __get_data_size(&tf->tp, regs); dsize = __get_data_size(&tf->tp, regs, entry_data);
entry = trace_event_buffer_reserve(&fbuffer, trace_file, entry = trace_event_buffer_reserve(&fbuffer, trace_file,
sizeof(*entry) + tf->tp.size + dsize); sizeof(*entry) + tf->tp.size + dsize);
...@@ -238,19 +255,19 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -238,19 +255,19 @@ __fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event); entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);
entry->func = entry_ip; entry->func = entry_ip;
entry->ret_ip = ret_ip; entry->ret_ip = ret_ip;
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tf->tp, regs, entry_data, sizeof(*entry), dsize);
trace_event_buffer_commit(&fbuffer); trace_event_buffer_commit(&fbuffer);
} }
static void static void
fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip, fexit_trace_func(struct trace_fprobe *tf, unsigned long entry_ip,
unsigned long ret_ip, struct pt_regs *regs) unsigned long ret_ip, struct pt_regs *regs, void *entry_data)
{ {
struct event_file_link *link; struct event_file_link *link;
trace_probe_for_each_link_rcu(link, &tf->tp) trace_probe_for_each_link_rcu(link, &tf->tp)
__fexit_trace_func(tf, entry_ip, ret_ip, regs, link->file); __fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data, link->file);
} }
NOKPROBE_SYMBOL(fexit_trace_func); NOKPROBE_SYMBOL(fexit_trace_func);
...@@ -269,7 +286,7 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -269,7 +286,7 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
if (hlist_empty(head)) if (hlist_empty(head))
return 0; return 0;
dsize = __get_data_size(&tf->tp, regs); dsize = __get_data_size(&tf->tp, regs, NULL);
__size = sizeof(*entry) + tf->tp.size + dsize; __size = sizeof(*entry) + tf->tp.size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64)); size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32); size -= sizeof(u32);
...@@ -280,7 +297,7 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -280,7 +297,7 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
entry->ip = entry_ip; entry->ip = entry_ip;
memset(&entry[1], 0, dsize); memset(&entry[1], 0, dsize);
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tf->tp, regs, NULL, sizeof(*entry), dsize);
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
head, NULL); head, NULL);
return 0; return 0;
...@@ -289,7 +306,8 @@ NOKPROBE_SYMBOL(fentry_perf_func); ...@@ -289,7 +306,8 @@ NOKPROBE_SYMBOL(fentry_perf_func);
static void static void
fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip, fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
unsigned long ret_ip, struct pt_regs *regs) unsigned long ret_ip, struct pt_regs *regs,
void *entry_data)
{ {
struct trace_event_call *call = trace_probe_event_call(&tf->tp); struct trace_event_call *call = trace_probe_event_call(&tf->tp);
struct fexit_trace_entry_head *entry; struct fexit_trace_entry_head *entry;
...@@ -301,7 +319,7 @@ fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -301,7 +319,7 @@ fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
if (hlist_empty(head)) if (hlist_empty(head))
return; return;
dsize = __get_data_size(&tf->tp, regs); dsize = __get_data_size(&tf->tp, regs, entry_data);
__size = sizeof(*entry) + tf->tp.size + dsize; __size = sizeof(*entry) + tf->tp.size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64)); size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32); size -= sizeof(u32);
...@@ -312,7 +330,7 @@ fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip, ...@@ -312,7 +330,7 @@ fexit_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
entry->func = entry_ip; entry->func = entry_ip;
entry->ret_ip = ret_ip; entry->ret_ip = ret_ip;
store_trace_args(&entry[1], &tf->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tf->tp, regs, entry_data, sizeof(*entry), dsize);
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
head, NULL); head, NULL);
} }
...@@ -343,10 +361,10 @@ static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip, ...@@ -343,10 +361,10 @@ static void fexit_dispatcher(struct fprobe *fp, unsigned long entry_ip,
struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp); struct trace_fprobe *tf = container_of(fp, struct trace_fprobe, fp);
if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE)) if (trace_probe_test_flag(&tf->tp, TP_FLAG_TRACE))
fexit_trace_func(tf, entry_ip, ret_ip, regs); fexit_trace_func(tf, entry_ip, ret_ip, regs, entry_data);
#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS
if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE)) if (trace_probe_test_flag(&tf->tp, TP_FLAG_PROFILE))
fexit_perf_func(tf, entry_ip, ret_ip, regs); fexit_perf_func(tf, entry_ip, ret_ip, regs, entry_data);
#endif #endif
} }
NOKPROBE_SYMBOL(fexit_dispatcher); NOKPROBE_SYMBOL(fexit_dispatcher);
...@@ -1109,6 +1127,11 @@ static int __trace_fprobe_create(int argc, const char *argv[]) ...@@ -1109,6 +1127,11 @@ static int __trace_fprobe_create(int argc, const char *argv[])
goto error; /* This can be -ENOMEM */ goto error; /* This can be -ENOMEM */
} }
if (is_return && tf->tp.entry_arg) {
tf->fp.entry_handler = trace_fprobe_entry_handler;
tf->fp.entry_data_size = traceprobe_get_entry_data_size(&tf->tp);
}
ret = traceprobe_set_print_fmt(&tf->tp, ret = traceprobe_set_print_fmt(&tf->tp,
is_return ? PROBE_PRINT_RETURN : PROBE_PRINT_NORMAL); is_return ? PROBE_PRINT_RETURN : PROBE_PRINT_NORMAL);
if (ret < 0) if (ret < 0)
......
...@@ -740,6 +740,9 @@ static unsigned int number_of_same_symbols(char *func_name) ...@@ -740,6 +740,9 @@ static unsigned int number_of_same_symbols(char *func_name)
return ctx.count; return ctx.count;
} }
static int trace_kprobe_entry_handler(struct kretprobe_instance *ri,
struct pt_regs *regs);
static int __trace_kprobe_create(int argc, const char *argv[]) static int __trace_kprobe_create(int argc, const char *argv[])
{ {
/* /*
...@@ -948,6 +951,11 @@ static int __trace_kprobe_create(int argc, const char *argv[]) ...@@ -948,6 +951,11 @@ static int __trace_kprobe_create(int argc, const char *argv[])
if (ret) if (ret)
goto error; /* This can be -ENOMEM */ goto error; /* This can be -ENOMEM */
} }
/* entry handler for kretprobe */
if (is_return && tk->tp.entry_arg) {
tk->rp.entry_handler = trace_kprobe_entry_handler;
tk->rp.data_size = traceprobe_get_entry_data_size(&tk->tp);
}
ptype = is_return ? PROBE_PRINT_RETURN : PROBE_PRINT_NORMAL; ptype = is_return ? PROBE_PRINT_RETURN : PROBE_PRINT_NORMAL;
ret = traceprobe_set_print_fmt(&tk->tp, ptype); ret = traceprobe_set_print_fmt(&tk->tp, ptype);
...@@ -1303,8 +1311,8 @@ static const struct file_operations kprobe_profile_ops = { ...@@ -1303,8 +1311,8 @@ static const struct file_operations kprobe_profile_ops = {
/* Note that we don't verify it, since the code does not come from user space */ /* Note that we don't verify it, since the code does not come from user space */
static int static int
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
void *base) void *dest, void *base)
{ {
struct pt_regs *regs = rec; struct pt_regs *regs = rec;
unsigned long val; unsigned long val;
...@@ -1329,6 +1337,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -1329,6 +1337,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
case FETCH_OP_ARG: case FETCH_OP_ARG:
val = regs_get_kernel_argument(regs, code->param); val = regs_get_kernel_argument(regs, code->param);
break; break;
case FETCH_OP_EDATA:
val = *(unsigned long *)((unsigned long)edata + code->offset);
break;
#endif #endif
case FETCH_NOP_SYMBOL: /* Ignore a place holder */ case FETCH_NOP_SYMBOL: /* Ignore a place holder */
code++; code++;
...@@ -1359,7 +1370,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs, ...@@ -1359,7 +1370,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
if (trace_trigger_soft_disabled(trace_file)) if (trace_trigger_soft_disabled(trace_file))
return; return;
dsize = __get_data_size(&tk->tp, regs); dsize = __get_data_size(&tk->tp, regs, NULL);
entry = trace_event_buffer_reserve(&fbuffer, trace_file, entry = trace_event_buffer_reserve(&fbuffer, trace_file,
sizeof(*entry) + tk->tp.size + dsize); sizeof(*entry) + tk->tp.size + dsize);
...@@ -1368,7 +1379,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs, ...@@ -1368,7 +1379,7 @@ __kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs,
fbuffer.regs = regs; fbuffer.regs = regs;
entry->ip = (unsigned long)tk->rp.kp.addr; entry->ip = (unsigned long)tk->rp.kp.addr;
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tk->tp, regs, NULL, sizeof(*entry), dsize);
trace_event_buffer_commit(&fbuffer); trace_event_buffer_commit(&fbuffer);
} }
...@@ -1384,6 +1395,31 @@ kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs) ...@@ -1384,6 +1395,31 @@ kprobe_trace_func(struct trace_kprobe *tk, struct pt_regs *regs)
NOKPROBE_SYMBOL(kprobe_trace_func); NOKPROBE_SYMBOL(kprobe_trace_func);
/* Kretprobe handler */ /* Kretprobe handler */
static int trace_kprobe_entry_handler(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct kretprobe *rp = get_kretprobe(ri);
struct trace_kprobe *tk;
/*
* There is a small chance that get_kretprobe(ri) returns NULL when
* the kretprobe is unregister on another CPU between kretprobe's
* trampoline_handler and this function.
*/
if (unlikely(!rp))
return -ENOENT;
tk = container_of(rp, struct trace_kprobe, rp);
/* store argument values into ri->data as entry data */
if (tk->tp.entry_arg)
store_trace_entry_data(ri->data, &tk->tp, regs);
return 0;
}
static nokprobe_inline void static nokprobe_inline void
__kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
struct pt_regs *regs, struct pt_regs *regs,
...@@ -1399,7 +1435,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, ...@@ -1399,7 +1435,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
if (trace_trigger_soft_disabled(trace_file)) if (trace_trigger_soft_disabled(trace_file))
return; return;
dsize = __get_data_size(&tk->tp, regs); dsize = __get_data_size(&tk->tp, regs, ri->data);
entry = trace_event_buffer_reserve(&fbuffer, trace_file, entry = trace_event_buffer_reserve(&fbuffer, trace_file,
sizeof(*entry) + tk->tp.size + dsize); sizeof(*entry) + tk->tp.size + dsize);
...@@ -1409,7 +1445,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, ...@@ -1409,7 +1445,7 @@ __kretprobe_trace_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
fbuffer.regs = regs; fbuffer.regs = regs;
entry->func = (unsigned long)tk->rp.kp.addr; entry->func = (unsigned long)tk->rp.kp.addr;
entry->ret_ip = get_kretprobe_retaddr(ri); entry->ret_ip = get_kretprobe_retaddr(ri);
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tk->tp, regs, ri->data, sizeof(*entry), dsize);
trace_event_buffer_commit(&fbuffer); trace_event_buffer_commit(&fbuffer);
} }
...@@ -1557,7 +1593,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) ...@@ -1557,7 +1593,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
if (hlist_empty(head)) if (hlist_empty(head))
return 0; return 0;
dsize = __get_data_size(&tk->tp, regs); dsize = __get_data_size(&tk->tp, regs, NULL);
__size = sizeof(*entry) + tk->tp.size + dsize; __size = sizeof(*entry) + tk->tp.size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64)); size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32); size -= sizeof(u32);
...@@ -1568,7 +1604,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) ...@@ -1568,7 +1604,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
entry->ip = (unsigned long)tk->rp.kp.addr; entry->ip = (unsigned long)tk->rp.kp.addr;
memset(&entry[1], 0, dsize); memset(&entry[1], 0, dsize);
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tk->tp, regs, NULL, sizeof(*entry), dsize);
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
head, NULL); head, NULL);
return 0; return 0;
...@@ -1593,7 +1629,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, ...@@ -1593,7 +1629,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
if (hlist_empty(head)) if (hlist_empty(head))
return; return;
dsize = __get_data_size(&tk->tp, regs); dsize = __get_data_size(&tk->tp, regs, ri->data);
__size = sizeof(*entry) + tk->tp.size + dsize; __size = sizeof(*entry) + tk->tp.size + dsize;
size = ALIGN(__size + sizeof(u32), sizeof(u64)); size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32); size -= sizeof(u32);
...@@ -1604,7 +1640,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, ...@@ -1604,7 +1640,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri,
entry->func = (unsigned long)tk->rp.kp.addr; entry->func = (unsigned long)tk->rp.kp.addr;
entry->ret_ip = get_kretprobe_retaddr(ri); entry->ret_ip = get_kretprobe_retaddr(ri);
store_trace_args(&entry[1], &tk->tp, regs, sizeof(*entry), dsize); store_trace_args(&entry[1], &tk->tp, regs, ri->data, sizeof(*entry), dsize);
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
head, NULL); head, NULL);
} }
......
...@@ -594,6 +594,8 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, ...@@ -594,6 +594,8 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type,
return 0; return 0;
} }
static int __store_entry_arg(struct trace_probe *tp, int argnum);
static int parse_btf_arg(char *varname, static int parse_btf_arg(char *varname,
struct fetch_insn **pcode, struct fetch_insn *end, struct fetch_insn **pcode, struct fetch_insn *end,
struct traceprobe_parse_context *ctx) struct traceprobe_parse_context *ctx)
...@@ -618,11 +620,7 @@ static int parse_btf_arg(char *varname, ...@@ -618,11 +620,7 @@ static int parse_btf_arg(char *varname,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
if (ctx->flags & TPARG_FL_RETURN) { if (ctx->flags & TPARG_FL_RETURN && !strcmp(varname, "$retval")) {
if (strcmp(varname, "$retval") != 0) {
trace_probe_log_err(ctx->offset, NO_BTFARG);
return -ENOENT;
}
code->op = FETCH_OP_RETVAL; code->op = FETCH_OP_RETVAL;
/* Check whether the function return type is not void */ /* Check whether the function return type is not void */
if (query_btf_context(ctx) == 0) { if (query_btf_context(ctx) == 0) {
...@@ -654,11 +652,21 @@ static int parse_btf_arg(char *varname, ...@@ -654,11 +652,21 @@ static int parse_btf_arg(char *varname,
const char *name = btf_name_by_offset(ctx->btf, params[i].name_off); const char *name = btf_name_by_offset(ctx->btf, params[i].name_off);
if (name && !strcmp(name, varname)) { if (name && !strcmp(name, varname)) {
code->op = FETCH_OP_ARG; if (tparg_is_function_entry(ctx->flags)) {
if (ctx->flags & TPARG_FL_TPOINT) code->op = FETCH_OP_ARG;
code->param = i + 1; if (ctx->flags & TPARG_FL_TPOINT)
else code->param = i + 1;
code->param = i; else
code->param = i;
} else if (tparg_is_function_return(ctx->flags)) {
code->op = FETCH_OP_EDATA;
ret = __store_entry_arg(ctx->tp, i);
if (ret < 0) {
/* internal error */
return ret;
}
code->offset = ret;
}
tid = params[i].type; tid = params[i].type;
goto found; goto found;
} }
...@@ -755,6 +763,110 @@ static int check_prepare_btf_string_fetch(char *typename, ...@@ -755,6 +763,110 @@ static int check_prepare_btf_string_fetch(char *typename,
#endif #endif
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
static int __store_entry_arg(struct trace_probe *tp, int argnum)
{
struct probe_entry_arg *earg = tp->entry_arg;
bool match = false;
int i, offset;
if (!earg) {
earg = kzalloc(sizeof(*tp->entry_arg), GFP_KERNEL);
if (!earg)
return -ENOMEM;
earg->size = 2 * tp->nr_args + 1;
earg->code = kcalloc(earg->size, sizeof(struct fetch_insn),
GFP_KERNEL);
if (!earg->code) {
kfree(earg);
return -ENOMEM;
}
/* Fill the code buffer with 'end' to simplify it */
for (i = 0; i < earg->size; i++)
earg->code[i].op = FETCH_OP_END;
tp->entry_arg = earg;
}
offset = 0;
for (i = 0; i < earg->size - 1; i++) {
switch (earg->code[i].op) {
case FETCH_OP_END:
earg->code[i].op = FETCH_OP_ARG;
earg->code[i].param = argnum;
earg->code[i + 1].op = FETCH_OP_ST_EDATA;
earg->code[i + 1].offset = offset;
return offset;
case FETCH_OP_ARG:
match = (earg->code[i].param == argnum);
break;
case FETCH_OP_ST_EDATA:
offset = earg->code[i].offset;
if (match)
return offset;
offset += sizeof(unsigned long);
break;
default:
break;
}
}
return -ENOSPC;
}
int traceprobe_get_entry_data_size(struct trace_probe *tp)
{
struct probe_entry_arg *earg = tp->entry_arg;
int i, size = 0;
if (!earg)
return 0;
for (i = 0; i < earg->size; i++) {
switch (earg->code[i].op) {
case FETCH_OP_END:
goto out;
case FETCH_OP_ST_EDATA:
size = earg->code[i].offset + sizeof(unsigned long);
break;
default:
break;
}
}
out:
return size;
}
void store_trace_entry_data(void *edata, struct trace_probe *tp, struct pt_regs *regs)
{
struct probe_entry_arg *earg = tp->entry_arg;
unsigned long val;
int i;
if (!earg)
return;
for (i = 0; i < earg->size; i++) {
struct fetch_insn *code = &earg->code[i];
switch (code->op) {
case FETCH_OP_ARG:
val = regs_get_kernel_argument(regs, code->param);
break;
case FETCH_OP_ST_EDATA:
*(unsigned long *)((unsigned long)edata + code->offset) = val;
break;
case FETCH_OP_END:
goto end;
default:
break;
}
}
end:
return;
}
NOKPROBE_SYMBOL(store_trace_entry_data)
#endif
#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
/* Parse $vars. @orig_arg points '$', which syncs to @ctx->offset */ /* Parse $vars. @orig_arg points '$', which syncs to @ctx->offset */
...@@ -830,7 +942,7 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, ...@@ -830,7 +942,7 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
len = str_has_prefix(arg, "arg"); len = str_has_prefix(arg, "arg");
if (len && tparg_is_function_entry(ctx->flags)) { if (len) {
ret = kstrtoul(arg + len, 10, &param); ret = kstrtoul(arg + len, 10, &param);
if (ret) if (ret)
goto inval; goto inval;
...@@ -839,15 +951,29 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, ...@@ -839,15 +951,29 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,
err = TP_ERR_BAD_ARG_NUM; err = TP_ERR_BAD_ARG_NUM;
goto inval; goto inval;
} }
param--; /* argN starts from 1, but internal arg[N] starts from 0 */
code->op = FETCH_OP_ARG; if (tparg_is_function_entry(ctx->flags)) {
code->param = (unsigned int)param - 1; code->op = FETCH_OP_ARG;
/* code->param = (unsigned int)param;
* The tracepoint probe will probe a stub function, and the /*
* first parameter of the stub is a dummy and should be ignored. * The tracepoint probe will probe a stub function, and the
*/ * first parameter of the stub is a dummy and should be ignored.
if (ctx->flags & TPARG_FL_TPOINT) */
code->param++; if (ctx->flags & TPARG_FL_TPOINT)
code->param++;
} else if (tparg_is_function_return(ctx->flags)) {
/* function entry argument access from return probe */
ret = __store_entry_arg(ctx->tp, param);
if (ret < 0) /* This error should be an internal error */
return ret;
code->op = FETCH_OP_EDATA;
code->offset = ret;
} else {
err = TP_ERR_NOFENTRY_ARGS;
goto inval;
}
return 0; return 0;
} }
#endif #endif
...@@ -1037,7 +1163,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ...@@ -1037,7 +1163,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
break; break;
default: default:
if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */ if (isalpha(arg[0]) || arg[0] == '_') { /* BTF variable */
if (!tparg_is_function_entry(ctx->flags)) { if (!tparg_is_function_entry(ctx->flags) &&
!tparg_is_function_return(ctx->flags)) {
trace_probe_log_err(ctx->offset, NOSUP_BTFARG); trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
return -EINVAL; return -EINVAL;
} }
...@@ -1423,6 +1550,7 @@ int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, const char *arg, ...@@ -1423,6 +1550,7 @@ int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, const char *arg,
struct probe_arg *parg = &tp->args[i]; struct probe_arg *parg = &tp->args[i];
const char *body; const char *body;
ctx->tp = tp;
body = strchr(arg, '='); body = strchr(arg, '=');
if (body) { if (body) {
if (body - arg > MAX_ARG_NAME_LEN) { if (body - arg > MAX_ARG_NAME_LEN) {
...@@ -1479,7 +1607,8 @@ static int argv_has_var_arg(int argc, const char *argv[], int *args_idx, ...@@ -1479,7 +1607,8 @@ static int argv_has_var_arg(int argc, const char *argv[], int *args_idx,
if (str_has_prefix(argv[i], "$arg")) { if (str_has_prefix(argv[i], "$arg")) {
trace_probe_log_set_index(i + 2); trace_probe_log_set_index(i + 2);
if (!tparg_is_function_entry(ctx->flags)) { if (!tparg_is_function_entry(ctx->flags) &&
!tparg_is_function_return(ctx->flags)) {
trace_probe_log_err(0, NOFENTRY_ARGS); trace_probe_log_err(0, NOFENTRY_ARGS);
return -EINVAL; return -EINVAL;
} }
...@@ -1802,6 +1931,12 @@ void trace_probe_cleanup(struct trace_probe *tp) ...@@ -1802,6 +1931,12 @@ void trace_probe_cleanup(struct trace_probe *tp)
for (i = 0; i < tp->nr_args; i++) for (i = 0; i < tp->nr_args; i++)
traceprobe_free_probe_arg(&tp->args[i]); traceprobe_free_probe_arg(&tp->args[i]);
if (tp->entry_arg) {
kfree(tp->entry_arg->code);
kfree(tp->entry_arg);
tp->entry_arg = NULL;
}
if (tp->event) if (tp->event)
trace_probe_unlink(tp); trace_probe_unlink(tp);
} }
......
...@@ -92,6 +92,7 @@ enum fetch_op { ...@@ -92,6 +92,7 @@ enum fetch_op {
FETCH_OP_ARG, /* Function argument : .param */ FETCH_OP_ARG, /* Function argument : .param */
FETCH_OP_FOFFS, /* File offset: .immediate */ FETCH_OP_FOFFS, /* File offset: .immediate */
FETCH_OP_DATA, /* Allocated data: .data */ FETCH_OP_DATA, /* Allocated data: .data */
FETCH_OP_EDATA, /* Entry data: .offset */
// Stage 2 (dereference) op // Stage 2 (dereference) op
FETCH_OP_DEREF, /* Dereference: .offset */ FETCH_OP_DEREF, /* Dereference: .offset */
FETCH_OP_UDEREF, /* User-space Dereference: .offset */ FETCH_OP_UDEREF, /* User-space Dereference: .offset */
...@@ -102,6 +103,7 @@ enum fetch_op { ...@@ -102,6 +103,7 @@ enum fetch_op {
FETCH_OP_ST_STRING, /* String: .offset, .size */ FETCH_OP_ST_STRING, /* String: .offset, .size */
FETCH_OP_ST_USTRING, /* User String: .offset, .size */ FETCH_OP_ST_USTRING, /* User String: .offset, .size */
FETCH_OP_ST_SYMSTR, /* Kernel Symbol String: .offset, .size */ FETCH_OP_ST_SYMSTR, /* Kernel Symbol String: .offset, .size */
FETCH_OP_ST_EDATA, /* Store Entry Data: .offset */
// Stage 4 (modify) op // Stage 4 (modify) op
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
// Stage 5 (loop) op // Stage 5 (loop) op
...@@ -232,6 +234,11 @@ struct probe_arg { ...@@ -232,6 +234,11 @@ struct probe_arg {
const struct fetch_type *type; /* Type of this argument */ const struct fetch_type *type; /* Type of this argument */
}; };
struct probe_entry_arg {
struct fetch_insn *code;
unsigned int size; /* The entry data size */
};
struct trace_uprobe_filter { struct trace_uprobe_filter {
rwlock_t rwlock; rwlock_t rwlock;
int nr_systemwide; int nr_systemwide;
...@@ -253,6 +260,7 @@ struct trace_probe { ...@@ -253,6 +260,7 @@ struct trace_probe {
struct trace_probe_event *event; struct trace_probe_event *event;
ssize_t size; /* trace entry size */ ssize_t size; /* trace entry size */
unsigned int nr_args; unsigned int nr_args;
struct probe_entry_arg *entry_arg; /* This is only for return probe */
struct probe_arg args[]; struct probe_arg args[];
}; };
...@@ -355,6 +363,18 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char ...@@ -355,6 +363,18 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_args, int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
u8 *data, void *field); u8 *data, void *field);
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
int traceprobe_get_entry_data_size(struct trace_probe *tp);
/* This is a runtime function to store entry data */
void store_trace_entry_data(void *edata, struct trace_probe *tp, struct pt_regs *regs);
#else /* !CONFIG_HAVE_FUNCTION_ARG_ACCESS_API */
static inline int traceprobe_get_entry_data_size(struct trace_probe *tp)
{
return 0;
}
#define store_trace_entry_data(edata, tp, regs) do { } while (0)
#endif
#define trace_probe_for_each_link(pos, tp) \ #define trace_probe_for_each_link(pos, tp) \
list_for_each_entry(pos, &(tp)->event->files, list) list_for_each_entry(pos, &(tp)->event->files, list)
#define trace_probe_for_each_link_rcu(pos, tp) \ #define trace_probe_for_each_link_rcu(pos, tp) \
...@@ -381,6 +401,11 @@ static inline bool tparg_is_function_entry(unsigned int flags) ...@@ -381,6 +401,11 @@ static inline bool tparg_is_function_entry(unsigned int flags)
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_FENTRY); return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_FENTRY);
} }
static inline bool tparg_is_function_return(unsigned int flags)
{
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_RETURN);
}
struct traceprobe_parse_context { struct traceprobe_parse_context {
struct trace_event_call *event; struct trace_event_call *event;
/* BTF related parameters */ /* BTF related parameters */
...@@ -392,6 +417,7 @@ struct traceprobe_parse_context { ...@@ -392,6 +417,7 @@ struct traceprobe_parse_context {
const struct btf_type *last_type; /* Saved type */ const struct btf_type *last_type; /* Saved type */
u32 last_bitoffs; /* Saved bitoffs */ u32 last_bitoffs; /* Saved bitoffs */
u32 last_bitsize; /* Saved bitsize */ u32 last_bitsize; /* Saved bitsize */
struct trace_probe *tp;
unsigned int flags; unsigned int flags;
int offset; int offset;
}; };
...@@ -506,7 +532,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -506,7 +532,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(NO_BTFARG, "This variable is not found at this probe point"),\ C(NO_BTFARG, "This variable is not found at this probe point"),\
C(NO_BTF_ENTRY, "No BTF entry for this probe point"), \ C(NO_BTF_ENTRY, "No BTF entry for this probe point"), \
C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\ C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\
C(NOFENTRY_ARGS, "$arg* can be used only on function entry"), \ C(NOFENTRY_ARGS, "$arg* can be used only on function entry or exit"), \
C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \ C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \
C(ARGS_2LONG, "$arg* failed because the argument list is too long"), \ C(ARGS_2LONG, "$arg* failed because the argument list is too long"), \
C(ARGIDX_2BIG, "$argN index is too big"), \ C(ARGIDX_2BIG, "$argN index is too big"), \
......
...@@ -54,7 +54,7 @@ fetch_apply_bitfield(struct fetch_insn *code, void *buf) ...@@ -54,7 +54,7 @@ fetch_apply_bitfield(struct fetch_insn *code, void *buf)
* If dest is NULL, don't store result and return required dynamic data size. * If dest is NULL, don't store result and return required dynamic data size.
*/ */
static int static int
process_fetch_insn(struct fetch_insn *code, void *rec, process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
void *dest, void *base); void *dest, void *base);
static nokprobe_inline int fetch_store_strlen(unsigned long addr); static nokprobe_inline int fetch_store_strlen(unsigned long addr);
static nokprobe_inline int static nokprobe_inline int
...@@ -232,7 +232,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -232,7 +232,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
/* Sum up total data length for dynamic arrays (strings) */ /* Sum up total data length for dynamic arrays (strings) */
static nokprobe_inline int static nokprobe_inline int
__get_data_size(struct trace_probe *tp, struct pt_regs *regs) __get_data_size(struct trace_probe *tp, struct pt_regs *regs, void *edata)
{ {
struct probe_arg *arg; struct probe_arg *arg;
int i, len, ret = 0; int i, len, ret = 0;
...@@ -240,7 +240,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs) ...@@ -240,7 +240,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
for (i = 0; i < tp->nr_args; i++) { for (i = 0; i < tp->nr_args; i++) {
arg = tp->args + i; arg = tp->args + i;
if (unlikely(arg->dynamic)) { if (unlikely(arg->dynamic)) {
len = process_fetch_insn(arg->code, regs, NULL, NULL); len = process_fetch_insn(arg->code, regs, edata, NULL, NULL);
if (len > 0) if (len > 0)
ret += len; ret += len;
} }
...@@ -251,7 +251,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs) ...@@ -251,7 +251,7 @@ __get_data_size(struct trace_probe *tp, struct pt_regs *regs)
/* Store the value of each argument */ /* Store the value of each argument */
static nokprobe_inline void static nokprobe_inline void
store_trace_args(void *data, struct trace_probe *tp, void *rec, store_trace_args(void *data, struct trace_probe *tp, void *rec, void *edata,
int header_size, int maxlen) int header_size, int maxlen)
{ {
struct probe_arg *arg; struct probe_arg *arg;
...@@ -266,7 +266,7 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec, ...@@ -266,7 +266,7 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec,
/* Point the dynamic data area if needed */ /* Point the dynamic data area if needed */
if (unlikely(arg->dynamic)) if (unlikely(arg->dynamic))
*dl = make_data_loc(maxlen, dyndata - base); *dl = make_data_loc(maxlen, dyndata - base);
ret = process_fetch_insn(arg->code, rec, dl, base); ret = process_fetch_insn(arg->code, rec, edata, dl, base);
if (arg->dynamic && likely(ret > 0)) { if (arg->dynamic && likely(ret > 0)) {
dyndata += ret; dyndata += ret;
maxlen -= ret; maxlen -= ret;
......
...@@ -211,8 +211,8 @@ static unsigned long translate_user_vaddr(unsigned long file_offset) ...@@ -211,8 +211,8 @@ static unsigned long translate_user_vaddr(unsigned long file_offset)
/* Note that we don't verify it, since the code does not come from user space */ /* Note that we don't verify it, since the code does not come from user space */
static int static int
process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, process_fetch_insn(struct fetch_insn *code, void *rec, void *edata,
void *base) void *dest, void *base)
{ {
struct pt_regs *regs = rec; struct pt_regs *regs = rec;
unsigned long val; unsigned long val;
...@@ -1490,11 +1490,11 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) ...@@ -1490,11 +1490,11 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
if (WARN_ON_ONCE(!uprobe_cpu_buffer)) if (WARN_ON_ONCE(!uprobe_cpu_buffer))
return 0; return 0;
dsize = __get_data_size(&tu->tp, regs); dsize = __get_data_size(&tu->tp, regs, NULL);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu)); esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
ucb = uprobe_buffer_get(); ucb = uprobe_buffer_get();
store_trace_args(ucb->buf, &tu->tp, regs, esize, dsize); store_trace_args(ucb->buf, &tu->tp, regs, NULL, esize, dsize);
if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE)) if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
ret |= uprobe_trace_func(tu, regs, ucb, dsize); ret |= uprobe_trace_func(tu, regs, ucb, dsize);
...@@ -1525,11 +1525,11 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con, ...@@ -1525,11 +1525,11 @@ static int uretprobe_dispatcher(struct uprobe_consumer *con,
if (WARN_ON_ONCE(!uprobe_cpu_buffer)) if (WARN_ON_ONCE(!uprobe_cpu_buffer))
return 0; return 0;
dsize = __get_data_size(&tu->tp, regs); dsize = __get_data_size(&tu->tp, regs, NULL);
esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu)); esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
ucb = uprobe_buffer_get(); ucb = uprobe_buffer_get();
store_trace_args(ucb->buf, &tu->tp, regs, esize, dsize); store_trace_args(ucb->buf, &tu->tp, regs, NULL, esize, dsize);
if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE)) if (trace_probe_test_flag(&tu->tp, TP_FLAG_TRACE))
uretprobe_trace_func(tu, func, regs, ucb, dsize); uretprobe_trace_func(tu, func, regs, ucb, dsize);
......
...@@ -34,7 +34,9 @@ check_error 'f vfs_read ^$stack10000' # BAD_STACK_NUM ...@@ -34,7 +34,9 @@ check_error 'f vfs_read ^$stack10000' # BAD_STACK_NUM
check_error 'f vfs_read ^$arg10000' # BAD_ARG_NUM check_error 'f vfs_read ^$arg10000' # BAD_ARG_NUM
if !grep -q 'kernel return probes support:' README; then
check_error 'f vfs_read $retval ^$arg1' # BAD_VAR check_error 'f vfs_read $retval ^$arg1' # BAD_VAR
fi
check_error 'f vfs_read ^$none_var' # BAD_VAR check_error 'f vfs_read ^$none_var' # BAD_VAR
check_error 'f vfs_read ^'$REG # BAD_VAR check_error 'f vfs_read ^'$REG # BAD_VAR
...@@ -99,7 +101,9 @@ if grep -q "<argname>" README; then ...@@ -99,7 +101,9 @@ if grep -q "<argname>" README; then
check_error 'f vfs_read args=^$arg*' # BAD_VAR_ARGS check_error 'f vfs_read args=^$arg*' # BAD_VAR_ARGS
check_error 'f vfs_read +0(^$arg*)' # BAD_VAR_ARGS check_error 'f vfs_read +0(^$arg*)' # BAD_VAR_ARGS
check_error 'f vfs_read $arg* ^$arg*' # DOUBLE_ARGS check_error 'f vfs_read $arg* ^$arg*' # DOUBLE_ARGS
if !grep -q 'kernel return probes support:' README; then
check_error 'f vfs_read%return ^$arg*' # NOFENTRY_ARGS check_error 'f vfs_read%return ^$arg*' # NOFENTRY_ARGS
fi
check_error 'f vfs_read ^hoge' # NO_BTFARG check_error 'f vfs_read ^hoge' # NO_BTFARG
check_error 'f kfree ^$arg10' # NO_BTFARG (exceed the number of parameters) check_error 'f kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
check_error 'f kfree%return ^$retval' # NO_RETVAL check_error 'f kfree%return ^$retval' # NO_RETVAL
......
...@@ -108,7 +108,9 @@ if grep -q "<argname>" README; then ...@@ -108,7 +108,9 @@ if grep -q "<argname>" README; then
check_error 'p vfs_read args=^$arg*' # BAD_VAR_ARGS check_error 'p vfs_read args=^$arg*' # BAD_VAR_ARGS
check_error 'p vfs_read +0(^$arg*)' # BAD_VAR_ARGS check_error 'p vfs_read +0(^$arg*)' # BAD_VAR_ARGS
check_error 'p vfs_read $arg* ^$arg*' # DOUBLE_ARGS check_error 'p vfs_read $arg* ^$arg*' # DOUBLE_ARGS
if !grep -q 'kernel return probes support:' README; then
check_error 'r vfs_read ^$arg*' # NOFENTRY_ARGS check_error 'r vfs_read ^$arg*' # NOFENTRY_ARGS
fi
check_error 'p vfs_read+8 ^$arg*' # NOFENTRY_ARGS check_error 'p vfs_read+8 ^$arg*' # NOFENTRY_ARGS
check_error 'p vfs_read ^hoge' # NO_BTFARG check_error 'p vfs_read ^hoge' # NO_BTFARG
check_error 'p kfree ^$arg10' # NO_BTFARG (exceed the number of parameters) check_error 'p kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
......
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