mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
Merge tag 'probes-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull probes updates from Masami Hiramatsu:
- kprobes: use struct_size() for variable size kretprobe_instance data
structure.
- eprobe: Simplify trace_eprobe list iteration.
- probe events: Data structure field access support on BTF argument.
- Update BTF argument support on the functions in the kernel
loadable modules (only loaded modules are supported).
- Move generic BTF access function (search function prototype and
get function parameters) to a separated file.
- Add a function to search a member of data structure in BTF.
- Support accessing BTF data structure member from probe args by
C-like arrow('->') and dot('.') operators. e.g.
't sched_switch next=next->pid vruntime=next->se.vruntime'
- Support accessing BTF data structure member from $retval. e.g.
'f getname_flags%return +0($retval->name):string'
- Add string type checking if BTF type info is available. This will
reject if user specify ":string" type for non "char pointer"
type.
- Automatically assume the fprobe event as a function return event
if $retval is used.
- selftests/ftrace: Add BTF data field access test cases.
- Documentation: Update fprobe event example with BTF data field.
* tag 'probes-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
Documentation: tracing: Update fprobe event example with BTF field
selftests/ftrace: Add BTF fields access testcases
tracing/fprobe-event: Assume fprobe is a return event by $retval
tracing/probes: Add string type check with BTF
tracing/probes: Support BTF field access from $retval
tracing/probes: Support BTF based data structure field access
tracing/probes: Add a function to search a member of a struct/union
tracing/probes: Move finding func-proto API and getting func-param API to trace_btf
tracing/probes: Support BTF argument on module functions
tracing/eprobe: Iterate trace_eprobe directly
kernel: kprobes: Use struct_size()
This commit is contained in:
@@ -79,9 +79,9 @@ automatically set by the given name. ::
|
||||
f:fprobes/myprobe vfs_read count=count pos=pos
|
||||
|
||||
It also chooses the fetch type from BTF information. For example, in the above
|
||||
example, the ``count`` is unsigned long, and the ``pos`` is a pointer. Thus, both
|
||||
are converted to 64bit unsigned long, but only ``pos`` has "%Lx" print-format as
|
||||
below ::
|
||||
example, the ``count`` is unsigned long, and the ``pos`` is a pointer. Thus,
|
||||
both are converted to 64bit unsigned long, but only ``pos`` has "%Lx"
|
||||
print-format as below ::
|
||||
|
||||
# cat events/fprobes/myprobe/format
|
||||
name: myprobe
|
||||
@@ -105,9 +105,47 @@ is expanded to all function arguments of the function or the tracepoint. ::
|
||||
# cat dynamic_events
|
||||
f:fprobes/myprobe vfs_read file=file buf=buf count=count pos=pos
|
||||
|
||||
BTF also affects the ``$retval``. If user doesn't set any type, the retval type is
|
||||
automatically picked from the BTF. If the function returns ``void``, ``$retval``
|
||||
is rejected.
|
||||
BTF also affects the ``$retval``. If user doesn't set any type, the retval
|
||||
type is automatically picked from the BTF. If the function returns ``void``,
|
||||
``$retval`` is rejected.
|
||||
|
||||
You can access the data fields of a data structure using allow operator ``->``
|
||||
(for pointer type) and dot operator ``.`` (for data structure type.)::
|
||||
|
||||
# echo 't sched_switch preempt prev_pid=prev->pid next_pid=next->pid' >> dynamic_events
|
||||
|
||||
The field access operators, ``->`` and ``.`` can be combined for accessing deeper
|
||||
members and other structure members pointed by the member. e.g. ``foo->bar.baz->qux``
|
||||
If there is non-name union member, you can directly access it as the C code does.
|
||||
For example::
|
||||
|
||||
struct {
|
||||
union {
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
} *foo;
|
||||
|
||||
To access ``a`` and ``b``, use ``foo->a`` and ``foo->b`` in this case.
|
||||
|
||||
This data field access is available for the return value via ``$retval``,
|
||||
e.g. ``$retval->name``.
|
||||
|
||||
For these BTF arguments and fields, ``:string`` and ``:ustring`` change the
|
||||
behavior. If these are used for BTF argument or field, it checks whether
|
||||
the BTF type of the argument or the data field is ``char *`` or ``char []``,
|
||||
or not. If not, it rejects applying the string types. Also, with the BTF
|
||||
support, you don't need a memory dereference operator (``+0(PTR)``) for
|
||||
accessing the string pointed by a ``PTR``. It automatically adds the memory
|
||||
dereference operator according to the BTF type. e.g. ::
|
||||
|
||||
# echo 't sched_switch prev->comm:string' >> dynamic_events
|
||||
# echo 'f getname_flags%return $retval->name:string' >> dynamic_events
|
||||
|
||||
The ``prev->comm`` is an embedded char array in the data structure, and
|
||||
``$retval->name`` is a char pointer in the data structure. But in both
|
||||
cases, you can use ``:string`` type to get the string.
|
||||
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
@@ -161,10 +199,10 @@ parameters. This means you can access any field values in the task
|
||||
structure pointed by the ``prev`` and ``next`` arguments.
|
||||
|
||||
For example, usually ``task_struct::start_time`` is not traced, but with this
|
||||
traceprobe event, you can trace it as below.
|
||||
traceprobe event, you can trace that field as below.
|
||||
::
|
||||
|
||||
# echo 't sched_switch comm=+1896(next):string start_time=+1728(next):u64' > dynamic_events
|
||||
# echo 't sched_switch comm=next->comm:string next->start_time' > dynamic_events
|
||||
# head -n 20 trace | tail
|
||||
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
|
||||
# | | | ||||| | |
|
||||
@@ -176,13 +214,3 @@ traceprobe event, you can trace it as below.
|
||||
<idle>-0 [000] d..3. 5606.690317: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="kworker/0:1" usage=1 start_time=137000000
|
||||
kworker/0:1-14 [000] d..3. 5606.690339: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="swapper/0" usage=2 start_time=0
|
||||
<idle>-0 [000] d..3. 5606.692368: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="kworker/0:1" usage=1 start_time=137000000
|
||||
|
||||
Currently, to find the offset of a specific field in the data structure,
|
||||
you need to build kernel with debuginfo and run `perf probe` command with
|
||||
`-D` option. e.g.
|
||||
::
|
||||
|
||||
# perf probe -D "__probestub_sched_switch next->comm:string next->start_time"
|
||||
p:probe/__probestub_sched_switch __probestub_sched_switch+0 comm=+1896(%cx):string start_time=+1728(%cx):u64
|
||||
|
||||
And replace the ``%cx`` with the ``next``.
|
||||
|
||||
@@ -209,6 +209,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type
|
||||
int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec);
|
||||
bool btf_type_is_void(const struct btf_type *t);
|
||||
s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind);
|
||||
s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p);
|
||||
const struct btf_type *btf_type_skip_modifiers(const struct btf *btf,
|
||||
u32 id, u32 *res_id);
|
||||
const struct btf_type *btf_type_resolve_ptr(const struct btf *btf,
|
||||
|
||||
@@ -553,7 +553,7 @@ s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p)
|
||||
s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p)
|
||||
{
|
||||
struct btf *btf;
|
||||
s32 ret;
|
||||
|
||||
@@ -2232,8 +2232,7 @@ int register_kretprobe(struct kretprobe *rp)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < rp->maxactive; i++) {
|
||||
inst = kzalloc(sizeof(struct kretprobe_instance) +
|
||||
rp->data_size, GFP_KERNEL);
|
||||
inst = kzalloc(struct_size(inst, data, rp->data_size), GFP_KERNEL);
|
||||
if (inst == NULL) {
|
||||
rethook_free(rp->rh);
|
||||
rp->rh = NULL;
|
||||
@@ -2256,8 +2255,7 @@ int register_kretprobe(struct kretprobe *rp)
|
||||
|
||||
rp->rph->rp = rp;
|
||||
for (i = 0; i < rp->maxactive; i++) {
|
||||
inst = kzalloc(sizeof(struct kretprobe_instance) +
|
||||
rp->data_size, GFP_KERNEL);
|
||||
inst = kzalloc(struct_size(inst, data, rp->data_size), GFP_KERNEL);
|
||||
if (inst == NULL) {
|
||||
refcount_set(&rp->rph->ref, i);
|
||||
free_rp_inst(rp);
|
||||
|
||||
@@ -99,6 +99,7 @@ obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
|
||||
endif
|
||||
obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o
|
||||
obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o
|
||||
obj-$(CONFIG_PROBE_EVENTS_BTF_ARGS) += trace_btf.o
|
||||
obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o
|
||||
obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o
|
||||
obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o
|
||||
|
||||
@@ -5711,7 +5711,8 @@ static const char readme_msg[] =
|
||||
"\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n"
|
||||
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
|
||||
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
|
||||
"\t $stack<index>, $stack, $retval, $comm, $arg<N>, <argname>\n"
|
||||
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
|
||||
"\t <argname>[->field[->field|.field...]],\n"
|
||||
#else
|
||||
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
|
||||
#endif
|
||||
|
||||
122
kernel/trace/trace_btf.c
Normal file
122
kernel/trace/trace_btf.c
Normal file
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/btf.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "trace_btf.h"
|
||||
|
||||
/*
|
||||
* Find a function proto type by name, and return the btf_type with its btf
|
||||
* in *@btf_p. Return NULL if not found.
|
||||
* Note that caller has to call btf_put(*@btf_p) after using the btf_type.
|
||||
*/
|
||||
const struct btf_type *btf_find_func_proto(const char *func_name, struct btf **btf_p)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
s32 id;
|
||||
|
||||
id = bpf_find_btf_id(func_name, BTF_KIND_FUNC, btf_p);
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
|
||||
/* Get BTF_KIND_FUNC type */
|
||||
t = btf_type_by_id(*btf_p, id);
|
||||
if (!t || !btf_type_is_func(t))
|
||||
goto err;
|
||||
|
||||
/* The type of BTF_KIND_FUNC is BTF_KIND_FUNC_PROTO */
|
||||
t = btf_type_by_id(*btf_p, t->type);
|
||||
if (!t || !btf_type_is_func_proto(t))
|
||||
goto err;
|
||||
|
||||
return t;
|
||||
err:
|
||||
btf_put(*btf_p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get function parameter with the number of parameters.
|
||||
* This can return NULL if the function has no parameters.
|
||||
* It can return -EINVAL if the @func_proto is not a function proto type.
|
||||
*/
|
||||
const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, s32 *nr)
|
||||
{
|
||||
if (!btf_type_is_func_proto(func_proto))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*nr = btf_type_vlen(func_proto);
|
||||
if (*nr > 0)
|
||||
return (const struct btf_param *)(func_proto + 1);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define BTF_ANON_STACK_MAX 16
|
||||
|
||||
struct btf_anon_stack {
|
||||
u32 tid;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
/*
|
||||
* Find a member of data structure/union by name and return it.
|
||||
* Return NULL if not found, or -EINVAL if parameter is invalid.
|
||||
* If the member is an member of anonymous union/structure, the offset
|
||||
* of that anonymous union/structure is stored into @anon_offset. Caller
|
||||
* can calculate the correct offset from the root data structure by
|
||||
* adding anon_offset to the member's offset.
|
||||
*/
|
||||
const struct btf_member *btf_find_struct_member(struct btf *btf,
|
||||
const struct btf_type *type,
|
||||
const char *member_name,
|
||||
u32 *anon_offset)
|
||||
{
|
||||
struct btf_anon_stack *anon_stack;
|
||||
const struct btf_member *member;
|
||||
u32 tid, cur_offset = 0;
|
||||
const char *name;
|
||||
int i, top = 0;
|
||||
|
||||
anon_stack = kcalloc(BTF_ANON_STACK_MAX, sizeof(*anon_stack), GFP_KERNEL);
|
||||
if (!anon_stack)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
retry:
|
||||
if (!btf_type_is_struct(type)) {
|
||||
member = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for_each_member(i, type, member) {
|
||||
if (!member->name_off) {
|
||||
/* Anonymous union/struct: push it for later use */
|
||||
type = btf_type_skip_modifiers(btf, member->type, &tid);
|
||||
if (type && top < BTF_ANON_STACK_MAX) {
|
||||
anon_stack[top].tid = tid;
|
||||
anon_stack[top++].offset =
|
||||
cur_offset + member->offset;
|
||||
}
|
||||
} else {
|
||||
name = btf_name_by_offset(btf, member->name_off);
|
||||
if (name && !strcmp(member_name, name)) {
|
||||
if (anon_offset)
|
||||
*anon_offset = cur_offset;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (top > 0) {
|
||||
/* Pop from the anonymous stack and retry */
|
||||
tid = anon_stack[--top].tid;
|
||||
cur_offset = anon_stack[top].offset;
|
||||
type = btf_type_by_id(btf, tid);
|
||||
goto retry;
|
||||
}
|
||||
member = NULL;
|
||||
|
||||
out:
|
||||
kfree(anon_stack);
|
||||
return member;
|
||||
}
|
||||
|
||||
11
kernel/trace/trace_btf.h
Normal file
11
kernel/trace/trace_btf.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/btf.h>
|
||||
|
||||
const struct btf_type *btf_find_func_proto(const char *func_name,
|
||||
struct btf **btf_p);
|
||||
const struct btf_param *btf_get_func_param(const struct btf_type *func_proto,
|
||||
s32 *nr);
|
||||
const struct btf_member *btf_find_struct_member(struct btf *btf,
|
||||
const struct btf_type *type,
|
||||
const char *member_name,
|
||||
u32 *anon_offset);
|
||||
@@ -41,6 +41,10 @@ struct eprobe_data {
|
||||
struct trace_eprobe *ep;
|
||||
};
|
||||
|
||||
|
||||
#define for_each_trace_eprobe_tp(ep, _tp) \
|
||||
list_for_each_entry(ep, trace_probe_probe_list(_tp), tp.list)
|
||||
|
||||
static int __trace_eprobe_create(int argc, const char *argv[]);
|
||||
|
||||
static void trace_event_probe_cleanup(struct trace_eprobe *ep)
|
||||
@@ -640,7 +644,7 @@ static int disable_eprobe(struct trace_eprobe *ep,
|
||||
static int enable_trace_eprobe(struct trace_event_call *call,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
struct trace_probe *pos, *tp;
|
||||
struct trace_probe *tp;
|
||||
struct trace_eprobe *ep;
|
||||
bool enabled;
|
||||
int ret = 0;
|
||||
@@ -662,8 +666,7 @@ static int enable_trace_eprobe(struct trace_event_call *call,
|
||||
if (enabled)
|
||||
return 0;
|
||||
|
||||
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
|
||||
ep = container_of(pos, struct trace_eprobe, tp);
|
||||
for_each_trace_eprobe_tp(ep, tp) {
|
||||
ret = enable_eprobe(ep, file);
|
||||
if (ret)
|
||||
break;
|
||||
@@ -680,8 +683,7 @@ static int enable_trace_eprobe(struct trace_event_call *call,
|
||||
*/
|
||||
WARN_ON_ONCE(ret != -ENOMEM);
|
||||
|
||||
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
|
||||
ep = container_of(pos, struct trace_eprobe, tp);
|
||||
for_each_trace_eprobe_tp(ep, tp) {
|
||||
disable_eprobe(ep, file->tr);
|
||||
if (!--cnt)
|
||||
break;
|
||||
@@ -699,7 +701,7 @@ static int enable_trace_eprobe(struct trace_event_call *call,
|
||||
static int disable_trace_eprobe(struct trace_event_call *call,
|
||||
struct trace_event_file *file)
|
||||
{
|
||||
struct trace_probe *pos, *tp;
|
||||
struct trace_probe *tp;
|
||||
struct trace_eprobe *ep;
|
||||
|
||||
tp = trace_probe_primary_from_call(call);
|
||||
@@ -716,10 +718,8 @@ static int disable_trace_eprobe(struct trace_event_call *call,
|
||||
trace_probe_clear_flag(tp, TP_FLAG_PROFILE);
|
||||
|
||||
if (!trace_probe_is_enabled(tp)) {
|
||||
list_for_each_entry(pos, trace_probe_probe_list(tp), list) {
|
||||
ep = container_of(pos, struct trace_eprobe, tp);
|
||||
for_each_trace_eprobe_tp(ep, tp)
|
||||
disable_eprobe(ep, file->tr);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -807,13 +807,11 @@ static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[
|
||||
int ret;
|
||||
|
||||
ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], &ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Handle symbols "@" */
|
||||
if (!ret)
|
||||
ret = traceprobe_update_arg(&ep->tp.args[i]);
|
||||
|
||||
traceprobe_finish_parse(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -898,6 +898,46 @@ static struct tracepoint *find_tracepoint(const char *tp_name)
|
||||
return data.tpoint;
|
||||
}
|
||||
|
||||
static int parse_symbol_and_return(int argc, const char *argv[],
|
||||
char **symbol, bool *is_return,
|
||||
bool is_tracepoint)
|
||||
{
|
||||
char *tmp = strchr(argv[1], '%');
|
||||
int i;
|
||||
|
||||
if (tmp) {
|
||||
int len = tmp - argv[1];
|
||||
|
||||
if (!is_tracepoint && !strcmp(tmp, "%return")) {
|
||||
*is_return = true;
|
||||
} else {
|
||||
trace_probe_log_err(len, BAD_ADDR_SUFFIX);
|
||||
return -EINVAL;
|
||||
}
|
||||
*symbol = kmemdup_nul(argv[1], len, GFP_KERNEL);
|
||||
} else
|
||||
*symbol = kstrdup(argv[1], GFP_KERNEL);
|
||||
if (!*symbol)
|
||||
return -ENOMEM;
|
||||
|
||||
if (*is_return)
|
||||
return 0;
|
||||
|
||||
/* If there is $retval, this should be a return fprobe. */
|
||||
for (i = 2; i < argc; i++) {
|
||||
tmp = strstr(argv[i], "$retval");
|
||||
if (tmp && !isalnum(tmp[7]) && tmp[7] != '_') {
|
||||
*is_return = true;
|
||||
/*
|
||||
* NOTE: Don't check is_tracepoint here, because it will
|
||||
* be checked when the argument is parsed.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __trace_fprobe_create(int argc, const char *argv[])
|
||||
{
|
||||
/*
|
||||
@@ -927,7 +967,7 @@ static int __trace_fprobe_create(int argc, const char *argv[])
|
||||
struct trace_fprobe *tf = NULL;
|
||||
int i, len, new_argc = 0, ret = 0;
|
||||
bool is_return = false;
|
||||
char *symbol = NULL, *tmp = NULL;
|
||||
char *symbol = NULL;
|
||||
const char *event = NULL, *group = FPROBE_EVENT_SYSTEM;
|
||||
const char **new_argv = NULL;
|
||||
int maxactive = 0;
|
||||
@@ -983,20 +1023,10 @@ static int __trace_fprobe_create(int argc, const char *argv[])
|
||||
trace_probe_log_set_index(1);
|
||||
|
||||
/* a symbol(or tracepoint) must be specified */
|
||||
symbol = kstrdup(argv[1], GFP_KERNEL);
|
||||
if (!symbol)
|
||||
return -ENOMEM;
|
||||
ret = parse_symbol_and_return(argc, argv, &symbol, &is_return, is_tracepoint);
|
||||
if (ret < 0)
|
||||
goto parse_error;
|
||||
|
||||
tmp = strchr(symbol, '%');
|
||||
if (tmp) {
|
||||
if (!is_tracepoint && !strcmp(tmp, "%return")) {
|
||||
*tmp = '\0';
|
||||
is_return = true;
|
||||
} else {
|
||||
trace_probe_log_err(tmp - symbol, BAD_ADDR_SUFFIX);
|
||||
goto parse_error;
|
||||
}
|
||||
}
|
||||
if (!is_return && maxactive) {
|
||||
trace_probe_log_set_index(0);
|
||||
trace_probe_log_err(1, BAD_MAXACT_TYPE);
|
||||
@@ -1096,6 +1126,7 @@ static int __trace_fprobe_create(int argc, const char *argv[])
|
||||
}
|
||||
|
||||
out:
|
||||
traceprobe_finish_parse(&ctx);
|
||||
trace_probe_log_clear();
|
||||
kfree(new_argv);
|
||||
kfree(symbol);
|
||||
|
||||
@@ -907,6 +907,7 @@ static int __trace_kprobe_create(int argc, const char *argv[])
|
||||
}
|
||||
|
||||
out:
|
||||
traceprobe_finish_parse(&ctx);
|
||||
trace_probe_log_clear();
|
||||
kfree(new_argv);
|
||||
kfree(symbol);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -383,9 +383,15 @@ static inline bool tparg_is_function_entry(unsigned int flags)
|
||||
|
||||
struct traceprobe_parse_context {
|
||||
struct trace_event_call *event;
|
||||
const struct btf_param *params;
|
||||
s32 nr_params;
|
||||
const char *funcname;
|
||||
/* BTF related parameters */
|
||||
const char *funcname; /* Function name in BTF */
|
||||
const struct btf_type *proto; /* Prototype of the function */
|
||||
const struct btf_param *params; /* Parameter of the function */
|
||||
s32 nr_params; /* The number of the parameters */
|
||||
struct btf *btf; /* The BTF to be used */
|
||||
const struct btf_type *last_type; /* Saved type */
|
||||
u32 last_bitoffs; /* Saved bitoffs */
|
||||
u32 last_bitsize; /* Saved bitsize */
|
||||
unsigned int flags;
|
||||
int offset;
|
||||
};
|
||||
@@ -400,6 +406,12 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[],
|
||||
extern int traceprobe_update_arg(struct probe_arg *arg);
|
||||
extern void traceprobe_free_probe_arg(struct probe_arg *arg);
|
||||
|
||||
/*
|
||||
* If either traceprobe_parse_probe_arg() or traceprobe_expand_meta_args() is called,
|
||||
* this MUST be called for clean up the context and return a resource.
|
||||
*/
|
||||
void traceprobe_finish_parse(struct traceprobe_parse_context *ctx);
|
||||
|
||||
extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
|
||||
int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
|
||||
char *buf, int offset);
|
||||
@@ -495,7 +507,14 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
|
||||
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(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(NO_PTR_STRCT, "This is not a pointer to union/structure."), \
|
||||
C(NOSUP_DAT_ARG, "Non pointer structure/union argument is not supported."),\
|
||||
C(BAD_HYPHEN, "Failed to parse single hyphen. Forgot '>'?"), \
|
||||
C(NO_BTF_FIELD, "This field is not found."), \
|
||||
C(BAD_BTF_TID, "Failed to get BTF type info."),\
|
||||
C(BAD_TYPE4STR, "This type does not fit for string."),
|
||||
|
||||
#undef C
|
||||
#define C(a, b) TP_ERR_##a
|
||||
|
||||
@@ -688,6 +688,7 @@ static int __trace_uprobe_create(int argc, const char **argv)
|
||||
|
||||
trace_probe_log_set_index(i + 2);
|
||||
ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], &ctx);
|
||||
traceprobe_finish_parse(&ctx);
|
||||
if (ret)
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
KPROBES=
|
||||
FPROBES=
|
||||
FIELDS=
|
||||
|
||||
if grep -qF "p[:[<group>/][<event>]] <place> [<args>]" README ; then
|
||||
KPROBES=yes
|
||||
@@ -12,6 +13,9 @@ fi
|
||||
if grep -qF "f[:[<group>/][<event>]] <func-name>[%return] [<args>]" README ; then
|
||||
FPROBES=yes
|
||||
fi
|
||||
if grep -qF "<argname>[->field[->field|.field...]]" README ; then
|
||||
FIELDS=yes
|
||||
fi
|
||||
|
||||
if [ -z "$KPROBES" -a -z "$FPROBES" ] ; then
|
||||
exit_unsupported
|
||||
@@ -21,6 +25,9 @@ echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
TP=kfree
|
||||
TP2=kmem_cache_alloc
|
||||
TP3=getname_flags
|
||||
TP4=sched_wakeup
|
||||
|
||||
if [ "$FPROBES" ] ; then
|
||||
echo "f:fpevent $TP object" >> dynamic_events
|
||||
@@ -33,6 +40,7 @@ echo > dynamic_events
|
||||
|
||||
echo "f:fpevent $TP "'$arg1' >> dynamic_events
|
||||
grep -q "fpevent.*object=object" dynamic_events
|
||||
|
||||
echo > dynamic_events
|
||||
|
||||
echo "f:fpevent $TP "'$arg*' >> dynamic_events
|
||||
@@ -45,6 +53,18 @@ fi
|
||||
|
||||
echo > dynamic_events
|
||||
|
||||
if [ "$FIELDS" ] ; then
|
||||
echo "t:tpevent ${TP2} obj_size=s->object_size" >> dynamic_events
|
||||
echo "f:fpevent ${TP3}%return path=\$retval->name:string" >> dynamic_events
|
||||
echo "t:tpevent2 ${TP4} p->se.group_node.next->prev" >> dynamic_events
|
||||
|
||||
grep -q "tpevent .*obj_size=s->object_size" dynamic_events
|
||||
grep -q "fpevent.*path=\$retval->name:string" dynamic_events
|
||||
grep -q 'tpevent2 .*p->se.group_node.next->prev' dynamic_events
|
||||
|
||||
echo > dynamic_events
|
||||
fi
|
||||
|
||||
if [ "$KPROBES" ] ; then
|
||||
echo "p:kpevent $TP object" >> dynamic_events
|
||||
grep -q "kpevent.*object=object" dynamic_events
|
||||
|
||||
@@ -30,11 +30,11 @@ check_error 'f:^ vfs_read' # NO_EVENT_NAME
|
||||
check_error 'f:foo/^12345678901234567890123456789012345678901234567890123456789012345 vfs_read' # EVENT_TOO_LONG
|
||||
check_error 'f:foo/^bar.1 vfs_read' # BAD_EVENT_NAME
|
||||
|
||||
check_error 'f vfs_read ^$retval' # RETVAL_ON_PROBE
|
||||
check_error 'f vfs_read ^$stack10000' # BAD_STACK_NUM
|
||||
|
||||
check_error 'f vfs_read ^$arg10000' # BAD_ARG_NUM
|
||||
|
||||
check_error 'f vfs_read $retval ^$arg1' # BAD_VAR
|
||||
check_error 'f vfs_read ^$none_var' # BAD_VAR
|
||||
check_error 'f vfs_read ^'$REG # BAD_VAR
|
||||
|
||||
@@ -103,6 +103,14 @@ check_error 'f vfs_read%return ^$arg*' # NOFENTRY_ARGS
|
||||
check_error 'f vfs_read ^hoge' # NO_BTFARG
|
||||
check_error 'f kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
|
||||
check_error 'f kfree%return ^$retval' # NO_RETVAL
|
||||
|
||||
if grep -qF "<argname>[->field[->field|.field...]]" README ; then
|
||||
check_error 'f vfs_read%return $retval->^foo' # NO_PTR_STRCT
|
||||
check_error 'f vfs_read file->^foo' # NO_BTF_FIELD
|
||||
check_error 'f vfs_read file^-.foo' # BAD_HYPHEN
|
||||
check_error 'f vfs_read ^file:string' # BAD_TYPE4STR
|
||||
fi
|
||||
|
||||
else
|
||||
check_error 'f vfs_read ^$arg*' # NOSUP_BTFARG
|
||||
check_error 't kfree ^$arg*' # NOSUP_BTFARG
|
||||
|
||||
Reference in New Issue
Block a user