probes cleanup updates for v6.3:
These are the probe events cleanup patches, no new features but improve readability. - Rename print_probe_args() to trace_probe_print_args() and un-inlined. - Introduce a set of default data fetch functions for dynamic probe events. - Extract common code of data fetch process of dynamic probe events. -----BEGIN PGP SIGNATURE----- iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmP40igbHG1hc2FtaS5o aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8b1Q4IAIdX/xWscmgnH/OjKlXK U/O/mzvgY4m8pGpxbTHQ0DLea6PW2SUmGuPz+9BvRMisNtJP3M/C3vpnYEZRwkY9 Movn0lCBZ47Dw2pdwBz34XKUI+B3Tp42/Xs5J8FaKlp+/CcJNqGpsJnF0Hm05MUb fZyQNI2hArnXITLHP+ADNiG6lTly0Zaza5nw27P+VW+6sfJhzuSG4hl+WXgHL8ut M0s6972QYH08uGs7Ywnl8pMpng6861YpHnREoOLOJ+1Cg6IYA+O6XK3Q75EXp+IX lXl611AAKZKpvc27eX+e9ionJRS4j1C0C12HSHYX/B8DN7SzuctXm1zmKewonHd5 r8M= =1Lix -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
116b41162f
@ -311,7 +311,7 @@ print_eprobe_event(struct trace_iterator *iter, int flags,
|
||||
|
||||
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)
|
||||
goto out;
|
||||
|
||||
@ -320,7 +320,8 @@ print_eprobe_event(struct trace_iterator *iter, int flags,
|
||||
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;
|
||||
unsigned long val;
|
||||
@ -395,20 +396,12 @@ static int get_eprobe_size(struct trace_probe *tp, void *rec)
|
||||
case FETCH_OP_TP_ARG:
|
||||
val = get_event_field(code, rec);
|
||||
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 */
|
||||
code++;
|
||||
goto retry;
|
||||
default:
|
||||
continue;
|
||||
if (process_common_fetch_insn(code, &val) < 0)
|
||||
continue;
|
||||
}
|
||||
code++;
|
||||
len = process_fetch_insn_bottom(code, val, NULL, NULL);
|
||||
@ -428,84 +421,26 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
|
||||
void *base)
|
||||
{
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
switch (code->op) {
|
||||
case FETCH_OP_TP_ARG:
|
||||
val = get_event_field(code, rec);
|
||||
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 */
|
||||
code++;
|
||||
goto retry;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
ret = process_common_fetch_insn(code, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
code++;
|
||||
return process_fetch_insn_bottom(code, val, dest, base);
|
||||
}
|
||||
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 */
|
||||
static inline void
|
||||
__eprobe_trace_func(struct eprobe_data *edata, void *rec)
|
||||
|
@ -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 += data_size;
|
||||
|
||||
len = kern_fetch_store_strlen((unsigned long)str_val);
|
||||
len = fetch_store_strlen((unsigned long)str_val);
|
||||
|
||||
data_offset |= len << 16;
|
||||
*(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)++;
|
||||
} else {
|
||||
@ -542,7 +542,7 @@ static notrace void trace_event_raw_event_synth(void *__data,
|
||||
len = *((unsigned long *)str_val);
|
||||
len *= sizeof(unsigned long);
|
||||
} else {
|
||||
len = kern_fetch_store_strlen((unsigned long)str_val);
|
||||
len = fetch_store_strlen((unsigned long)str_val);
|
||||
}
|
||||
|
||||
fields_size += len;
|
||||
|
@ -1218,60 +1218,6 @@ static const struct file_operations kprobe_profile_ops = {
|
||||
.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 */
|
||||
static int
|
||||
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;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
retry:
|
||||
/* 1st stage: get value from context */
|
||||
@ -1295,15 +1242,6 @@ retry:
|
||||
case FETCH_OP_RETVAL:
|
||||
val = regs_return_value(regs);
|
||||
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
|
||||
case FETCH_OP_ARG:
|
||||
val = regs_get_kernel_argument(regs, code->param);
|
||||
@ -1313,7 +1251,9 @@ retry:
|
||||
code++;
|
||||
goto retry;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
ret = process_common_fetch_insn(code, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
code++;
|
||||
|
||||
@ -1424,7 +1364,7 @@ print_kprobe_event(struct trace_iterator *iter, int flags,
|
||||
|
||||
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)
|
||||
goto out;
|
||||
|
||||
@ -1459,7 +1399,7 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,
|
||||
|
||||
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)
|
||||
goto out;
|
||||
|
||||
|
@ -1239,3 +1239,30 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
|
||||
|
||||
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);
|
||||
bool trace_probe_match_command_args(struct trace_probe *tp,
|
||||
int argc, const char **argv);
|
||||
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) \
|
||||
list_for_each_entry(pos, &(tp)->event->files, list)
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
/* Return the length of string -- including null terminal byte */
|
||||
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;
|
||||
int ret;
|
||||
@ -29,14 +29,14 @@ kern_fetch_store_strlen_user(unsigned long addr)
|
||||
|
||||
/* Return the length of string -- including null terminal byte */
|
||||
static nokprobe_inline int
|
||||
kern_fetch_store_strlen(unsigned long addr)
|
||||
fetch_store_strlen(unsigned long addr)
|
||||
{
|
||||
int ret, len = 0;
|
||||
u8 c;
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
if (addr < TASK_SIZE)
|
||||
return kern_fetch_store_strlen_user(addr);
|
||||
return fetch_store_strlen_user(addr);
|
||||
#endif
|
||||
|
||||
do {
|
||||
@ -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.
|
||||
*/
|
||||
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;
|
||||
int maxlen = get_loc_len(*(u32 *)dest);
|
||||
@ -86,7 +86,7 @@ kern_fetch_store_string_user(unsigned long addr, void *dest, void *base)
|
||||
* length and relative data location.
|
||||
*/
|
||||
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);
|
||||
void *__dest;
|
||||
@ -94,7 +94,7 @@ kern_fetch_store_string(unsigned long addr, void *dest, void *base)
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE
|
||||
if ((unsigned long)addr < TASK_SIZE)
|
||||
return kern_fetch_store_string_user(addr, dest, base);
|
||||
return fetch_store_string_user(addr, dest, base);
|
||||
#endif
|
||||
|
||||
if (unlikely(!maxlen))
|
||||
@ -112,4 +112,22 @@ kern_fetch_store_string(unsigned long addr, void *dest, void *base)
|
||||
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_ */
|
||||
|
@ -98,6 +98,26 @@ fetch_store_symstring(unsigned long addr, void *dest, void *base)
|
||||
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 */
|
||||
static nokprobe_inline int
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
{
|
||||
struct pt_regs *regs = rec;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
/* 1st stage: get value from context */
|
||||
switch (code->op) {
|
||||
@ -235,20 +236,16 @@ process_fetch_insn(struct fetch_insn *code, void *rec, void *dest,
|
||||
case FETCH_OP_RETVAL:
|
||||
val = regs_return_value(regs);
|
||||
break;
|
||||
case FETCH_OP_IMM:
|
||||
val = code->immediate;
|
||||
break;
|
||||
case FETCH_OP_COMM:
|
||||
val = FETCH_TOKEN_COMM;
|
||||
break;
|
||||
case FETCH_OP_DATA:
|
||||
val = (unsigned long)code->data;
|
||||
break;
|
||||
case FETCH_OP_FOFFS:
|
||||
val = translate_user_vaddr(code->immediate);
|
||||
break;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
ret = process_common_fetch_insn(code, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
code++;
|
||||
|
||||
@ -1042,7 +1039,7 @@ print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *e
|
||||
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;
|
||||
|
||||
trace_seq_putc(s, '\n');
|
||||
|
Loading…
Reference in New Issue
Block a user