Commit 116b4116 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'probes-v6.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull kprobes cleanup updates from Masami Hiramatsu:
 "These are probe events cleanups, no new features but improve
  readability:

   - Rename print_probe_args() to trace_probe_print_args() and
     un-inline it

   - Introduce a set of default data fetch functions for dynamic
     probe events

   - Extract common code of data fetch process of dynamic probe events"

* tag 'probes-v6.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  kernel/trace: extract common part in process_fetch_insn
  kernel/trace: Provide default impelentations defined in trace_probe_tmpl.h
  kernel/trace: Introduce trace_probe_print_args and use it in *probes
parents 0447ed0d bd78acc8
...@@ -311,7 +311,7 @@ print_eprobe_event(struct trace_iterator *iter, int flags, ...@@ -311,7 +311,7 @@ print_eprobe_event(struct trace_iterator *iter, int flags,
trace_seq_putc(s, ')'); trace_seq_putc(s, ')');
if (print_probe_args(s, tp->args, tp->nr_args, if (trace_probe_print_args(s, tp->args, tp->nr_args,
(u8 *)&field[1], field) < 0) (u8 *)&field[1], field) < 0)
goto out; goto out;
...@@ -320,7 +320,8 @@ print_eprobe_event(struct trace_iterator *iter, int flags, ...@@ -320,7 +320,8 @@ print_eprobe_event(struct trace_iterator *iter, int flags,
return trace_handle_return(s); return trace_handle_return(s);
} }
static unsigned long get_event_field(struct fetch_insn *code, void *rec) static nokprobe_inline unsigned long
get_event_field(struct fetch_insn *code, void *rec)
{ {
struct ftrace_event_field *field = code->data; struct ftrace_event_field *field = code->data;
unsigned long val; unsigned long val;
...@@ -395,20 +396,12 @@ static int get_eprobe_size(struct trace_probe *tp, void *rec) ...@@ -395,20 +396,12 @@ static int get_eprobe_size(struct trace_probe *tp, void *rec)
case FETCH_OP_TP_ARG: case FETCH_OP_TP_ARG:
val = get_event_field(code, rec); val = get_event_field(code, rec);
break; break;
case FETCH_OP_IMM:
val = code->immediate;
break;
case FETCH_OP_COMM:
val = (unsigned long)current->comm;
break;
case FETCH_OP_DATA:
val = (unsigned long)code->data;
break;
case FETCH_NOP_SYMBOL: /* Ignore a place holder */ case FETCH_NOP_SYMBOL: /* Ignore a place holder */
code++; code++;
goto retry; goto retry;
default: default:
continue; if (process_common_fetch_insn(code, &val) < 0)
continue;
} }
code++; code++;
len = process_fetch_insn_bottom(code, val, NULL, NULL); len = process_fetch_insn_bottom(code, val, NULL, NULL);
...@@ -428,84 +421,26 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -428,84 +421,26 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
void *base) void *base)
{ {
unsigned long val; unsigned long val;
int ret;
retry: retry:
switch (code->op) { switch (code->op) {
case FETCH_OP_TP_ARG: case FETCH_OP_TP_ARG:
val = get_event_field(code, rec); val = get_event_field(code, rec);
break; break;
case FETCH_OP_IMM:
val = code->immediate;
break;
case FETCH_OP_COMM:
val = (unsigned long)current->comm;
break;
case FETCH_OP_DATA:
val = (unsigned long)code->data;
break;
case FETCH_NOP_SYMBOL: /* Ignore a place holder */ case FETCH_NOP_SYMBOL: /* Ignore a place holder */
code++; code++;
goto retry; goto retry;
default: default:
return -EILSEQ; ret = process_common_fetch_insn(code, &val);
if (ret < 0)
return ret;
} }
code++; code++;
return process_fetch_insn_bottom(code, val, dest, base); return process_fetch_insn_bottom(code, val, dest, base);
} }
NOKPROBE_SYMBOL(process_fetch_insn) NOKPROBE_SYMBOL(process_fetch_insn)
/* Return the length of string -- including null terminal byte */
static nokprobe_inline int
fetch_store_strlen_user(unsigned long addr)
{
return kern_fetch_store_strlen_user(addr);
}
/* Return the length of string -- including null terminal byte */
static nokprobe_inline int
fetch_store_strlen(unsigned long addr)
{
return kern_fetch_store_strlen(addr);
}
/*
* Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf
* with max length and relative data location.
*/
static nokprobe_inline int
fetch_store_string_user(unsigned long addr, void *dest, void *base)
{
return kern_fetch_store_string_user(addr, dest, base);
}
/*
* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
* length and relative data location.
*/
static nokprobe_inline int
fetch_store_string(unsigned long addr, void *dest, void *base)
{
return kern_fetch_store_string(addr, dest, base);
}
static nokprobe_inline int
probe_mem_read_user(void *dest, void *src, size_t size)
{
const void __user *uaddr = (__force const void __user *)src;
return copy_from_user_nofault(dest, uaddr, size);
}
static nokprobe_inline int
probe_mem_read(void *dest, void *src, size_t size)
{
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if ((unsigned long)src < TASK_SIZE)
return probe_mem_read_user(dest, src, size);
#endif
return copy_from_kernel_nofault(dest, src, size);
}
/* eprobe handler */ /* eprobe handler */
static inline void static inline void
__eprobe_trace_func(struct eprobe_data *edata, void *rec) __eprobe_trace_func(struct eprobe_data *edata, void *rec)
......
...@@ -448,12 +448,12 @@ static unsigned int trace_string(struct synth_trace_event *entry, ...@@ -448,12 +448,12 @@ static unsigned int trace_string(struct synth_trace_event *entry,
data_offset = struct_size(entry, fields, event->n_u64); data_offset = struct_size(entry, fields, event->n_u64);
data_offset += data_size; data_offset += data_size;
len = kern_fetch_store_strlen((unsigned long)str_val); len = fetch_store_strlen((unsigned long)str_val);
data_offset |= len << 16; data_offset |= len << 16;
*(u32 *)&entry->fields[*n_u64] = data_offset; *(u32 *)&entry->fields[*n_u64] = data_offset;
ret = kern_fetch_store_string((unsigned long)str_val, &entry->fields[*n_u64], entry); ret = fetch_store_string((unsigned long)str_val, &entry->fields[*n_u64], entry);
(*n_u64)++; (*n_u64)++;
} else { } else {
...@@ -542,7 +542,7 @@ static notrace void trace_event_raw_event_synth(void *__data, ...@@ -542,7 +542,7 @@ static notrace void trace_event_raw_event_synth(void *__data,
len = *((unsigned long *)str_val); len = *((unsigned long *)str_val);
len *= sizeof(unsigned long); len *= sizeof(unsigned long);
} else { } else {
len = kern_fetch_store_strlen((unsigned long)str_val); len = fetch_store_strlen((unsigned long)str_val);
} }
fields_size += len; fields_size += len;
......
...@@ -1218,60 +1218,6 @@ static const struct file_operations kprobe_profile_ops = { ...@@ -1218,60 +1218,6 @@ static const struct file_operations kprobe_profile_ops = {
.release = seq_release, .release = seq_release,
}; };
/* Kprobe specific fetch functions */
/* Return the length of string -- including null terminal byte */
static nokprobe_inline int
fetch_store_strlen_user(unsigned long addr)
{
return kern_fetch_store_strlen_user(addr);
}
/* Return the length of string -- including null terminal byte */
static nokprobe_inline int
fetch_store_strlen(unsigned long addr)
{
return kern_fetch_store_strlen(addr);
}
/*
* Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf
* with max length and relative data location.
*/
static nokprobe_inline int
fetch_store_string_user(unsigned long addr, void *dest, void *base)
{
return kern_fetch_store_string_user(addr, dest, base);
}
/*
* Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max
* length and relative data location.
*/
static nokprobe_inline int
fetch_store_string(unsigned long addr, void *dest, void *base)
{
return kern_fetch_store_string(addr, dest, base);
}
static nokprobe_inline int
probe_mem_read_user(void *dest, void *src, size_t size)
{
const void __user *uaddr = (__force const void __user *)src;
return copy_from_user_nofault(dest, uaddr, size);
}
static nokprobe_inline int
probe_mem_read(void *dest, void *src, size_t size)
{
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if ((unsigned long)src < TASK_SIZE)
return probe_mem_read_user(dest, src, size);
#endif
return copy_from_kernel_nofault(dest, src, size);
}
/* 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 *dest,
...@@ -1279,6 +1225,7 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -1279,6 +1225,7 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
{ {
struct pt_regs *regs = rec; struct pt_regs *regs = rec;
unsigned long val; unsigned long val;
int ret;
retry: retry:
/* 1st stage: get value from context */ /* 1st stage: get value from context */
...@@ -1295,15 +1242,6 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -1295,15 +1242,6 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
case FETCH_OP_RETVAL: case FETCH_OP_RETVAL:
val = regs_return_value(regs); val = regs_return_value(regs);
break; break;
case FETCH_OP_IMM:
val = code->immediate;
break;
case FETCH_OP_COMM:
val = (unsigned long)current->comm;
break;
case FETCH_OP_DATA:
val = (unsigned long)code->data;
break;
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
case FETCH_OP_ARG: case FETCH_OP_ARG:
val = regs_get_kernel_argument(regs, code->param); val = regs_get_kernel_argument(regs, code->param);
...@@ -1313,7 +1251,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -1313,7 +1251,9 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
code++; code++;
goto retry; goto retry;
default: default:
return -EILSEQ; ret = process_common_fetch_insn(code, &val);
if (ret < 0)
return ret;
} }
code++; code++;
...@@ -1424,7 +1364,7 @@ print_kprobe_event(struct trace_iterator *iter, int flags, ...@@ -1424,7 +1364,7 @@ print_kprobe_event(struct trace_iterator *iter, int flags,
trace_seq_putc(s, ')'); trace_seq_putc(s, ')');
if (print_probe_args(s, tp->args, tp->nr_args, if (trace_probe_print_args(s, tp->args, tp->nr_args,
(u8 *)&field[1], field) < 0) (u8 *)&field[1], field) < 0)
goto out; goto out;
...@@ -1459,7 +1399,7 @@ print_kretprobe_event(struct trace_iterator *iter, int flags, ...@@ -1459,7 +1399,7 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,
trace_seq_putc(s, ')'); trace_seq_putc(s, ')');
if (print_probe_args(s, tp->args, tp->nr_args, if (trace_probe_print_args(s, tp->args, tp->nr_args,
(u8 *)&field[1], field) < 0) (u8 *)&field[1], field) < 0)
goto out; goto out;
......
...@@ -1239,3 +1239,30 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char ...@@ -1239,3 +1239,30 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
return ret; return ret;
} }
int trace_probe_print_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
u8 *data, void *field)
{
void *p;
int i, j;
for (i = 0; i < nr_args; i++) {
struct probe_arg *a = args + i;
trace_seq_printf(s, " %s=", a->name);
if (likely(!a->count)) {
if (!a->type->print(s, data + a->offset, field))
return -ENOMEM;
continue;
}
trace_seq_putc(s, '{');
p = data + a->offset;
for (j = 0; j < a->count; j++) {
if (!a->type->print(s, p, field))
return -ENOMEM;
trace_seq_putc(s, j == a->count - 1 ? '}' : ',');
p += a->type->size;
}
}
return 0;
}
...@@ -349,6 +349,8 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b); ...@@ -349,6 +349,8 @@ int trace_probe_compare_arg_type(struct trace_probe *a, struct trace_probe *b);
bool trace_probe_match_command_args(struct trace_probe *tp, bool trace_probe_match_command_args(struct trace_probe *tp,
int argc, const char **argv); int argc, const char **argv);
int trace_probe_create(const char *raw_command, int (*createfn)(int, const char **)); 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,
u8 *data, void *field);
#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)
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
*/ */
/* Return the length of string -- including null terminal byte */ /* Return the length of string -- including null terminal byte */
static nokprobe_inline int static nokprobe_inline int
kern_fetch_store_strlen_user(unsigned long addr) fetch_store_strlen_user(unsigned long addr)
{ {
const void __user *uaddr = (__force const void __user *)addr; const void __user *uaddr = (__force const void __user *)addr;
int ret; int ret;
...@@ -29,14 +29,14 @@ kern_fetch_store_strlen_user(unsigned long addr) ...@@ -29,14 +29,14 @@ kern_fetch_store_strlen_user(unsigned long addr)
/* Return the length of string -- including null terminal byte */ /* Return the length of string -- including null terminal byte */
static nokprobe_inline int static nokprobe_inline int
kern_fetch_store_strlen(unsigned long addr) fetch_store_strlen(unsigned long addr)
{ {
int ret, len = 0; int ret, len = 0;
u8 c; u8 c;
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if (addr < TASK_SIZE) if (addr < TASK_SIZE)
return kern_fetch_store_strlen_user(addr); return fetch_store_strlen_user(addr);
#endif #endif
do { do {
...@@ -63,7 +63,7 @@ static nokprobe_inline void set_data_loc(int ret, void *dest, void *__dest, void ...@@ -63,7 +63,7 @@ static nokprobe_inline void set_data_loc(int ret, void *dest, void *__dest, void
* with max length and relative data location. * with max length and relative data location.
*/ */
static nokprobe_inline int static nokprobe_inline int
kern_fetch_store_string_user(unsigned long addr, void *dest, void *base) fetch_store_string_user(unsigned long addr, void *dest, void *base)
{ {
const void __user *uaddr = (__force const void __user *)addr; const void __user *uaddr = (__force const void __user *)addr;
int maxlen = get_loc_len(*(u32 *)dest); int maxlen = get_loc_len(*(u32 *)dest);
...@@ -86,7 +86,7 @@ kern_fetch_store_string_user(unsigned long addr, void *dest, void *base) ...@@ -86,7 +86,7 @@ kern_fetch_store_string_user(unsigned long addr, void *dest, void *base)
* length and relative data location. * length and relative data location.
*/ */
static nokprobe_inline int static nokprobe_inline int
kern_fetch_store_string(unsigned long addr, void *dest, void *base) fetch_store_string(unsigned long addr, void *dest, void *base)
{ {
int maxlen = get_loc_len(*(u32 *)dest); int maxlen = get_loc_len(*(u32 *)dest);
void *__dest; void *__dest;
...@@ -94,7 +94,7 @@ kern_fetch_store_string(unsigned long addr, void *dest, void *base) ...@@ -94,7 +94,7 @@ kern_fetch_store_string(unsigned long addr, void *dest, void *base)
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE #ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if ((unsigned long)addr < TASK_SIZE) if ((unsigned long)addr < TASK_SIZE)
return kern_fetch_store_string_user(addr, dest, base); return fetch_store_string_user(addr, dest, base);
#endif #endif
if (unlikely(!maxlen)) if (unlikely(!maxlen))
...@@ -112,4 +112,22 @@ kern_fetch_store_string(unsigned long addr, void *dest, void *base) ...@@ -112,4 +112,22 @@ kern_fetch_store_string(unsigned long addr, void *dest, void *base)
return ret; return ret;
} }
static nokprobe_inline int
probe_mem_read_user(void *dest, void *src, size_t size)
{
const void __user *uaddr = (__force const void __user *)src;
return copy_from_user_nofault(dest, uaddr, size);
}
static nokprobe_inline int
probe_mem_read(void *dest, void *src, size_t size)
{
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
if ((unsigned long)src < TASK_SIZE)
return probe_mem_read_user(dest, src, size);
#endif
return copy_from_kernel_nofault(dest, src, size);
}
#endif /* __TRACE_PROBE_KERNEL_H_ */ #endif /* __TRACE_PROBE_KERNEL_H_ */
...@@ -98,6 +98,26 @@ fetch_store_symstring(unsigned long addr, void *dest, void *base) ...@@ -98,6 +98,26 @@ fetch_store_symstring(unsigned long addr, void *dest, void *base)
return sprint_symbol(__dest, addr); return sprint_symbol(__dest, addr);
} }
/* common part of process_fetch_insn*/
static nokprobe_inline int
process_common_fetch_insn(struct fetch_insn *code, unsigned long *val)
{
switch (code->op) {
case FETCH_OP_IMM:
*val = code->immediate;
break;
case FETCH_OP_COMM:
*val = (unsigned long)current->comm;
break;
case FETCH_OP_DATA:
*val = (unsigned long)code->data;
break;
default:
return -EILSEQ;
}
return 0;
}
/* From the 2nd stage, routine is same */ /* From the 2nd stage, routine is same */
static nokprobe_inline int static nokprobe_inline int
process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
...@@ -253,31 +273,3 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec, ...@@ -253,31 +273,3 @@ store_trace_args(void *data, struct trace_probe *tp, void *rec,
} }
} }
} }
static inline int
print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
u8 *data, void *field)
{
void *p;
int i, j;
for (i = 0; i < nr_args; i++) {
struct probe_arg *a = args + i;
trace_seq_printf(s, " %s=", a->name);
if (likely(!a->count)) {
if (!a->type->print(s, data + a->offset, field))
return -ENOMEM;
continue;
}
trace_seq_putc(s, '{');
p = data + a->offset;
for (j = 0; j < a->count; j++) {
if (!a->type->print(s, p, field))
return -ENOMEM;
trace_seq_putc(s, j == a->count - 1 ? '}' : ',');
p += a->type->size;
}
}
return 0;
}
...@@ -220,6 +220,7 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -220,6 +220,7 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
{ {
struct pt_regs *regs = rec; struct pt_regs *regs = rec;
unsigned long val; unsigned long val;
int ret;
/* 1st stage: get value from context */ /* 1st stage: get value from context */
switch (code->op) { switch (code->op) {
...@@ -235,20 +236,16 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest, ...@@ -235,20 +236,16 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
case FETCH_OP_RETVAL: case FETCH_OP_RETVAL:
val = regs_return_value(regs); val = regs_return_value(regs);
break; break;
case FETCH_OP_IMM:
val = code->immediate;
break;
case FETCH_OP_COMM: case FETCH_OP_COMM:
val = FETCH_TOKEN_COMM; val = FETCH_TOKEN_COMM;
break; break;
case FETCH_OP_DATA:
val = (unsigned long)code->data;
break;
case FETCH_OP_FOFFS: case FETCH_OP_FOFFS:
val = translate_user_vaddr(code->immediate); val = translate_user_vaddr(code->immediate);
break; break;
default: default:
return -EILSEQ; ret = process_common_fetch_insn(code, &val);
if (ret < 0)
return ret;
} }
code++; code++;
...@@ -1042,7 +1039,7 @@ print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *e ...@@ -1042,7 +1039,7 @@ print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *e
data = DATAOF_TRACE_ENTRY(entry, false); data = DATAOF_TRACE_ENTRY(entry, false);
} }
if (print_probe_args(s, tu->tp.args, tu->tp.nr_args, data, entry) < 0) if (trace_probe_print_args(s, tu->tp.args, tu->tp.nr_args, data, entry) < 0)
goto out; goto out;
trace_seq_putc(s, '\n'); trace_seq_putc(s, '\n');
......
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