mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 05:32:00 +00:00
Trace probes updates for 6.2:
- New "symstr" type for dynamic events that writes the name of the function+offset into the ring buffer and not just the address - Prevent kernel symbol processing on addresses in user space probes (uprobes). - And minor fixes and clean ups -----BEGIN PGP SIGNATURE----- iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCY5yAHxQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qoWoAP9ZLmqgIqlH3Zcms31SR250kLXxsxT3 JHe82hiuI1I3fAD/Z93QLHw9wngLqIMx/wXsdFjTNOGGWdxfclSWI2qI6Q0= =KaJg -----END PGP SIGNATURE----- Merge tag 'trace-probes-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull trace probes updates from Steven Rostedt: - New "symstr" type for dynamic events that writes the name of the function+offset into the ring buffer and not just the address - Prevent kernel symbol processing on addresses in user space probes (uprobes). - And minor fixes and clean ups * tag 'trace-probes-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: tracing/probes: Reject symbol/symstr type for uprobe tracing/probes: Add symstr type for dynamic events kprobes: kretprobe events missing on 2-core KVM guest kprobes: Fix check for probe enabled in kill_kprobe() test_kprobes: Fix implicit declaration error of test_kprobes tracing: Fix race where eprobes can be called before the event
This commit is contained in:
commit
af9b3fa15d
@ -131,8 +131,7 @@ For example, if the function is non-recursive and is called with a
|
||||
spinlock held, maxactive = 1 should be enough. If the function is
|
||||
non-recursive and can never relinquish the CPU (e.g., via a semaphore
|
||||
or preemption), NR_CPUS should be enough. If maxactive <= 0, it is
|
||||
set to a default value. If CONFIG_PREEMPT is enabled, the default
|
||||
is max(10, 2*NR_CPUS). Otherwise, the default is NR_CPUS.
|
||||
set to a default value: max(10, 2*NR_CPUS).
|
||||
|
||||
It's not a disaster if you set maxactive too low; you'll just miss
|
||||
some probes. In the kretprobe struct, the nmissed field is set to
|
||||
|
@ -58,8 +58,8 @@ Synopsis of kprobe_events
|
||||
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
|
||||
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
|
||||
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
|
||||
(x8/x16/x32/x64), "string", "ustring" and bitfield
|
||||
are supported.
|
||||
(x8/x16/x32/x64), "string", "ustring", "symbol", "symstr"
|
||||
and bitfield are supported.
|
||||
|
||||
(\*1) only for the probe on function entry (offs == 0).
|
||||
(\*2) only for return probe.
|
||||
@ -96,6 +96,10 @@ offset, and container-size (usually 32). The syntax is::
|
||||
|
||||
Symbol type('symbol') is an alias of u32 or u64 type (depends on BITS_PER_LONG)
|
||||
which shows given pointer in "symbol+offset" style.
|
||||
On the other hand, symbol-string type ('symstr') converts the given address to
|
||||
"symbol+offset/symbolsize" style and stores it as a null-terminated string.
|
||||
With 'symstr' type, you can filter the event with wildcard pattern of the
|
||||
symbols, and you don't need to solve symbol name by yourself.
|
||||
For $comm, the default type is "string"; any other type is invalid.
|
||||
|
||||
.. _user_mem_access:
|
||||
|
@ -2213,13 +2213,9 @@ int register_kretprobe(struct kretprobe *rp)
|
||||
rp->kp.post_handler = NULL;
|
||||
|
||||
/* Pre-allocate memory for max kretprobe instances */
|
||||
if (rp->maxactive <= 0) {
|
||||
#ifdef CONFIG_PREEMPTION
|
||||
if (rp->maxactive <= 0)
|
||||
rp->maxactive = max_t(unsigned int, 10, 2*num_possible_cpus());
|
||||
#else
|
||||
rp->maxactive = num_possible_cpus();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KRETPROBE_ON_RETHOOK
|
||||
rp->rh = rethook_alloc((void *)rp, kretprobe_rethook_handler);
|
||||
if (!rp->rh)
|
||||
@ -2364,6 +2360,14 @@ static void kill_kprobe(struct kprobe *p)
|
||||
|
||||
lockdep_assert_held(&kprobe_mutex);
|
||||
|
||||
/*
|
||||
* The module is going away. We should disarm the kprobe which
|
||||
* is using ftrace, because ftrace framework is still available at
|
||||
* 'MODULE_STATE_GOING' notification.
|
||||
*/
|
||||
if (kprobe_ftrace(p) && !kprobe_disabled(p) && !kprobes_all_disarmed)
|
||||
disarm_kprobe_ftrace(p);
|
||||
|
||||
p->flags |= KPROBE_FLAG_GONE;
|
||||
if (kprobe_aggrprobe(p)) {
|
||||
/*
|
||||
@ -2380,14 +2384,6 @@ static void kill_kprobe(struct kprobe *p)
|
||||
* the original probed function (which will be freed soon) any more.
|
||||
*/
|
||||
arch_remove_kprobe(p);
|
||||
|
||||
/*
|
||||
* The module is going away. We should disarm the kprobe which
|
||||
* is using ftrace, because ftrace framework is still available at
|
||||
* 'MODULE_STATE_GOING' notification.
|
||||
*/
|
||||
if (kprobe_ftrace(p) && !kprobe_disabled(p) && !kprobes_all_disarmed)
|
||||
disarm_kprobe_ftrace(p);
|
||||
}
|
||||
|
||||
/* Disable one kprobe */
|
||||
|
@ -5617,7 +5617,7 @@ static const char readme_msg[] =
|
||||
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
|
||||
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
|
||||
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
|
||||
"\t <type>\\[<array-size>\\]\n"
|
||||
"\t symstr, <type>\\[<array-size>\\]\n"
|
||||
#ifdef CONFIG_HIST_TRIGGERS
|
||||
"\t field: <stype> <name>;\n"
|
||||
"\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n"
|
||||
|
@ -76,9 +76,11 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
|
||||
/* Fetch type information table */
|
||||
static const struct fetch_type probe_fetch_types[] = {
|
||||
/* Special types */
|
||||
__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1,
|
||||
__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1,
|
||||
"__data_loc char[]"),
|
||||
__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1,
|
||||
__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1,
|
||||
"__data_loc char[]"),
|
||||
__ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1,
|
||||
"__data_loc char[]"),
|
||||
/* Basic types */
|
||||
ASSIGN_FETCH_TYPE(u8, u8, 0),
|
||||
@ -98,10 +100,15 @@ static const struct fetch_type probe_fetch_types[] = {
|
||||
ASSIGN_FETCH_TYPE_END
|
||||
};
|
||||
|
||||
static const struct fetch_type *find_fetch_type(const char *type)
|
||||
static const struct fetch_type *find_fetch_type(const char *type, unsigned long flags)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Reject the symbol/symstr for uprobes */
|
||||
if (type && (flags & TPARG_FL_USER) &&
|
||||
(!strcmp(type, "symbol") || !strcmp(type, "symstr")))
|
||||
return NULL;
|
||||
|
||||
if (!type)
|
||||
type = DEFAULT_FETCH_TYPE_STR;
|
||||
|
||||
@ -119,13 +126,13 @@ static const struct fetch_type *find_fetch_type(const char *type)
|
||||
|
||||
switch (bs) {
|
||||
case 8:
|
||||
return find_fetch_type("u8");
|
||||
return find_fetch_type("u8", flags);
|
||||
case 16:
|
||||
return find_fetch_type("u16");
|
||||
return find_fetch_type("u16", flags);
|
||||
case 32:
|
||||
return find_fetch_type("u32");
|
||||
return find_fetch_type("u32", flags);
|
||||
case 64:
|
||||
return find_fetch_type("u64");
|
||||
return find_fetch_type("u64", flags);
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
@ -478,7 +485,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
|
||||
DEREF_OPEN_BRACE);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
const struct fetch_type *t2 = find_fetch_type(NULL);
|
||||
const struct fetch_type *t2 = find_fetch_type(NULL, flags);
|
||||
|
||||
*tmp = '\0';
|
||||
ret = parse_probe_arg(arg, t2, &code, end, flags, offs);
|
||||
@ -630,9 +637,9 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
|
||||
/* The type of $comm must be "string", and not an array. */
|
||||
if (parg->count || (t && strcmp(t, "string")))
|
||||
goto out;
|
||||
parg->type = find_fetch_type("string");
|
||||
parg->type = find_fetch_type("string", flags);
|
||||
} else
|
||||
parg->type = find_fetch_type(t);
|
||||
parg->type = find_fetch_type(t, flags);
|
||||
if (!parg->type) {
|
||||
trace_probe_log_err(offset + (t ? (t - arg) : 0), BAD_TYPE);
|
||||
goto out;
|
||||
@ -662,16 +669,26 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
|
||||
|
||||
ret = -EINVAL;
|
||||
/* Store operation */
|
||||
if (!strcmp(parg->type->name, "string") ||
|
||||
!strcmp(parg->type->name, "ustring")) {
|
||||
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
|
||||
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
|
||||
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
|
||||
trace_probe_log_err(offset + (t ? (t - arg) : 0),
|
||||
BAD_STRING);
|
||||
goto fail;
|
||||
if (parg->type->is_string) {
|
||||
if (!strcmp(parg->type->name, "symstr")) {
|
||||
if (code->op != FETCH_OP_REG && code->op != FETCH_OP_STACK &&
|
||||
code->op != FETCH_OP_RETVAL && code->op != FETCH_OP_ARG &&
|
||||
code->op != FETCH_OP_DEREF && code->op != FETCH_OP_TP_ARG) {
|
||||
trace_probe_log_err(offset + (t ? (t - arg) : 0),
|
||||
BAD_SYMSTRING);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
|
||||
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
|
||||
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
|
||||
trace_probe_log_err(offset + (t ? (t - arg) : 0),
|
||||
BAD_STRING);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
|
||||
if (!strcmp(parg->type->name, "symstr") ||
|
||||
(code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
|
||||
code->op == FETCH_OP_DATA) || code->op == FETCH_OP_TP_ARG ||
|
||||
parg->count) {
|
||||
/*
|
||||
@ -679,6 +696,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
|
||||
* must be kept, and if parg->count != 0, this is an
|
||||
* array of string pointers instead of string address
|
||||
* itself.
|
||||
* For the symstr, it doesn't need to dereference, thus
|
||||
* it just get the value.
|
||||
*/
|
||||
code++;
|
||||
if (code->op != FETCH_OP_NOP) {
|
||||
@ -690,6 +709,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
|
||||
if (!strcmp(parg->type->name, "ustring") ||
|
||||
code->op == FETCH_OP_UDEREF)
|
||||
code->op = FETCH_OP_ST_USTRING;
|
||||
else if (!strcmp(parg->type->name, "symstr"))
|
||||
code->op = FETCH_OP_ST_SYMSTR;
|
||||
else
|
||||
code->op = FETCH_OP_ST_STRING;
|
||||
code->size = parg->type->size;
|
||||
@ -919,8 +940,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
|
||||
for (i = 0; i < tp->nr_args; i++) {
|
||||
parg = tp->args + i;
|
||||
if (parg->count) {
|
||||
if ((strcmp(parg->type->name, "string") == 0) ||
|
||||
(strcmp(parg->type->name, "ustring") == 0))
|
||||
if (parg->type->is_string)
|
||||
fmt = ", __get_str(%s[%d])";
|
||||
else
|
||||
fmt = ", REC->%s[%d]";
|
||||
@ -928,8 +948,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
|
||||
pos += snprintf(buf + pos, LEN_OR_ZERO,
|
||||
fmt, parg->name, j);
|
||||
} else {
|
||||
if ((strcmp(parg->type->name, "string") == 0) ||
|
||||
(strcmp(parg->type->name, "ustring") == 0))
|
||||
if (parg->type->is_string)
|
||||
fmt = ", __get_str(%s)";
|
||||
else
|
||||
fmt = ", REC->%s";
|
||||
|
@ -98,6 +98,7 @@ enum fetch_op {
|
||||
FETCH_OP_ST_UMEM, /* Mem: .offset, .size */
|
||||
FETCH_OP_ST_STRING, /* String: .offset, .size */
|
||||
FETCH_OP_ST_USTRING, /* User String: .offset, .size */
|
||||
FETCH_OP_ST_SYMSTR, /* Kernel Symbol String: .offset, .size */
|
||||
// Stage 4 (modify) op
|
||||
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
|
||||
// Stage 5 (loop) op
|
||||
@ -133,7 +134,8 @@ struct fetch_insn {
|
||||
struct fetch_type {
|
||||
const char *name; /* Name of type */
|
||||
size_t size; /* Byte size of type */
|
||||
int is_signed; /* Signed flag */
|
||||
bool is_signed; /* Signed flag */
|
||||
bool is_string; /* String flag */
|
||||
print_type_func_t print; /* Print functions */
|
||||
const char *fmt; /* Format string */
|
||||
const char *fmttype; /* Name in format file */
|
||||
@ -177,16 +179,19 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
|
||||
#define _ADDR_FETCH_TYPE(t) __ADDR_FETCH_TYPE(t)
|
||||
#define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG)
|
||||
|
||||
#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
|
||||
{.name = _name, \
|
||||
#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, str, _fmttype) \
|
||||
{.name = _name, \
|
||||
.size = _size, \
|
||||
.is_signed = sign, \
|
||||
.is_signed = (bool)sign, \
|
||||
.is_string = (bool)str, \
|
||||
.print = PRINT_TYPE_FUNC_NAME(ptype), \
|
||||
.fmt = PRINT_TYPE_FMT_NAME(ptype), \
|
||||
.fmttype = _fmttype, \
|
||||
}
|
||||
|
||||
/* Non string types can use these macros */
|
||||
#define _ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
|
||||
__ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, #_fmttype)
|
||||
__ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, 0, #_fmttype)
|
||||
#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
|
||||
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype)
|
||||
|
||||
@ -353,7 +358,8 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
|
||||
#define TPARG_FL_KERNEL BIT(1)
|
||||
#define TPARG_FL_FENTRY BIT(2)
|
||||
#define TPARG_FL_TPOINT BIT(3)
|
||||
#define TPARG_FL_MASK GENMASK(3, 0)
|
||||
#define TPARG_FL_USER BIT(4)
|
||||
#define TPARG_FL_MASK GENMASK(4, 0)
|
||||
|
||||
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
|
||||
const char *argv, unsigned int flags);
|
||||
@ -431,6 +437,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
|
||||
C(ARRAY_TOO_BIG, "Array number is too big"), \
|
||||
C(BAD_TYPE, "Unknown type is specified"), \
|
||||
C(BAD_STRING, "String accepts only memory argument"), \
|
||||
C(BAD_SYMSTRING, "Symbol String doesn't accept data/userdata"), \
|
||||
C(BAD_BITFIELD, "Invalid bitfield"), \
|
||||
C(ARG_NAME_TOO_LONG, "Argument name is too long"), \
|
||||
C(NO_ARG_NAME, "Argument name is not specified"), \
|
||||
|
@ -67,6 +67,37 @@ probe_mem_read(void *dest, void *src, size_t size);
|
||||
static nokprobe_inline int
|
||||
probe_mem_read_user(void *dest, void *src, size_t size);
|
||||
|
||||
static nokprobe_inline int
|
||||
fetch_store_symstrlen(unsigned long addr)
|
||||
{
|
||||
char namebuf[KSYM_SYMBOL_LEN];
|
||||
int ret;
|
||||
|
||||
ret = sprint_symbol(namebuf, addr);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return ret + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a null-terminated symbol string + offset. Caller MUST set *(u32 *)buf
|
||||
* with max length and relative data location.
|
||||
*/
|
||||
static nokprobe_inline int
|
||||
fetch_store_symstring(unsigned long addr, void *dest, void *base)
|
||||
{
|
||||
int maxlen = get_loc_len(*(u32 *)dest);
|
||||
void *__dest;
|
||||
|
||||
if (unlikely(!maxlen))
|
||||
return -ENOMEM;
|
||||
|
||||
__dest = get_loc_data(dest, base);
|
||||
|
||||
return sprint_symbol(__dest, addr);
|
||||
}
|
||||
|
||||
/* From the 2nd stage, routine is same */
|
||||
static nokprobe_inline int
|
||||
process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
|
||||
@ -99,16 +130,22 @@ stage2:
|
||||
stage3:
|
||||
/* 3rd stage: store value to buffer */
|
||||
if (unlikely(!dest)) {
|
||||
if (code->op == FETCH_OP_ST_STRING) {
|
||||
switch (code->op) {
|
||||
case FETCH_OP_ST_STRING:
|
||||
ret = fetch_store_strlen(val + code->offset);
|
||||
code++;
|
||||
goto array;
|
||||
} else if (code->op == FETCH_OP_ST_USTRING) {
|
||||
case FETCH_OP_ST_USTRING:
|
||||
ret += fetch_store_strlen_user(val + code->offset);
|
||||
code++;
|
||||
goto array;
|
||||
} else
|
||||
case FETCH_OP_ST_SYMSTR:
|
||||
ret += fetch_store_symstrlen(val + code->offset);
|
||||
code++;
|
||||
goto array;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
}
|
||||
}
|
||||
|
||||
switch (code->op) {
|
||||
@ -129,6 +166,10 @@ stage3:
|
||||
loc = *(u32 *)dest;
|
||||
ret = fetch_store_string_user(val + code->offset, dest, base);
|
||||
break;
|
||||
case FETCH_OP_ST_SYMSTR:
|
||||
loc = *(u32 *)dest;
|
||||
ret = fetch_store_symstring(val + code->offset, dest, base);
|
||||
break;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
@ -691,7 +691,8 @@ static int __trace_uprobe_create(int argc, const char **argv)
|
||||
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
|
||||
trace_probe_log_set_index(i + 2);
|
||||
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i],
|
||||
is_return ? TPARG_FL_RETURN : 0);
|
||||
(is_return ? TPARG_FL_RETURN : 0) |
|
||||
TPARG_FL_USER);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
|
@ -23,4 +23,9 @@ check_error 'p /bin/sh:10^%hoge' # BAD_ADDR_SUFFIX
|
||||
check_error 'p /bin/sh:10(10)^%return' # BAD_REFCNT_SUFFIX
|
||||
fi
|
||||
|
||||
# symstr is not supported by uprobe
|
||||
if grep -q ".*symstr.*" README; then
|
||||
check_error 'p /bin/sh:10 $stack0:^symstr' # BAD_TYPE
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
Loading…
Reference in New Issue
Block a user