mirror of
https://github.com/torvalds/linux.git
synced 2026-09-12 04:23:03 +02:00
Probes updates for v7.3:
- probes: Advanced BTF typecasting and variable fetch enhancements
. Typecast support across probe events: Extended BTF typecasting syntax
(e.g., (STRUCT)PARAM->MEMBER) to kprobes, uprobes, and fprobes on
function entry and return.
. Nested typecasts: Added support for chaining and nesting typecasts up
to 3 levels, including casting registers and stack variables.
. Field specifier option: Added (STRUCT,FIELD) syntax to emulate
container_of(), allowing retrieval of parent structures from member
pointers.
. $current variable support: Introduced $current special variable to
access the running task_struct via BTF dereferencing.
. Per-CPU variable access: Added this_cpu_read() and this_cpu_ptr()
fetcharg methods to trace CPU-local data safely.
. Fetcharg bytecode dumper: Added CONFIG_PROBE_EVENTS_DUMP_FETCHARG to
dump the compiled fetcharg bytecode instructions as comments in
dynamic_events.
. Extended symbol name handling: Removed the MAX_COMMON_HEAD_LEN limit
and extended MAX_ARGSTR_LEN to 256 bytes, enabling probing of long
symbols, mangled Rust symbols and complex BTF expressions.
. eprobe variable syntax: Allowed eprobes to reference event fields
directly without requiring a '$' prefix.
. Cleanup unused parameters, redundant codes, duplicate macros and
pointer arithmetic.
. Use a ternary operator for simplifying fetch_type_from_btf_type().
- bootconfig / boottime-trace: Expanded dynamic probe support
. Added boot-time tracing configuration support for event probes
(eprobes), function probes (fprobes), and tracepoint probes
(tprobes).
. ftrace2bootconfig: Allowed comment lines ('#') in dynamic_events file.
- fprobe / kprobe: Optimization, robustness, and cleanups
. fprobe: Simplified fprobe_remove_ips() by reusing graph and ftrace
helpers.
. fprobe: Removed __packed attribute from struct __fprobe_header to avoid
unaligned memory access penalties on RISC architectures.
. kprobe & fprobe: Removed redundant memset() calls in perf event probe
handlers.
. kprobes: Replaced legacy __ASSEMBLY__ with __ASSEMBLER__ in header
files.
- selftests & refactoring:
. Refactored parse_probe_arg() and parse_probe_vars(), and eliminated
recursion in probe argument parsing to protect kernel stack depth.
. Added selftests for BTF typecasts and module probing without module
prefixes.
. Forced LC_ALL=C in ftracetest to prevent test failures on localized
systems.
. Refactored btf_type_skip_modifiers() to remove ignored id parameter.
. Sorted ERRORS list in trace_probe.h alphabetically.
. Fixed typo in fprobe docs, and trace_fprobe function name.
. Renamed FETCH_OP_DATA to FETCH_OP_IMMSTR.
. Made file offset error message probe-agnostic.
-----BEGIN PGP SIGNATURE-----
iQFPBAABCgA5FiEEh7BulGwFlgAOi5DV2/sHvwUrPxsFAmqEbbEbHG1hc2FtaS5o
aXJhbWF0c3VAZ21haWwuY29tAAoJENv7B78FKz8bpXsH/i61CqF8bt3/BNUG5qTV
JP/j/Znl9utOq4+g01AeSt6tJREEp262w/b4ZwdXiu+Q41VYwyGazY93FxtX3rQC
gcYnJcAGghzNUjf7ujI2VXDYa11t+GiYSR3Jv8hc/Xu7rq/ilExLObFwJfdhNZAb
go7mC0x9TgRAyw/k/9ezwukBcXQUsuoBU3+IFvict9BrSVmvMPNFhCyvIi6Ps5Fj
NldKTDAaWiKUOuIBDF1vXXSbHMoec31qNll/Ps4HG8RLpdrSWbW4nTOaazir2bbS
RA6hUlVNwsYn7UN6x/taV2szojyAODDv1r6ULplCVjbsn5M0TcdmLFW8JUimaAIr
wm0=
=6KXI
-----END PGP SIGNATURE-----
Merge tag 'probes-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull probes updates from Masami Hiramatsu:
"BTF typecasting and variable fetch enhancements:
- Typecast support across probe events: Extended BTF typecasting
syntax (e.g., (STRUCT)PARAM->MEMBER) to kprobes, uprobes, and
fprobes on function entry and return
- Nested typecasts: Added support for chaining and nesting typecasts
up to 3 levels, including casting registers and stack variables
- Field specifier option: Added (STRUCT,FIELD) syntax to emulate
container_of(), allowing retrieval of parent structures from member
pointers
- $current variable support: Introduced $current special variable to
access the running task_struct via BTF dereferencing
- Per-CPU variable access: Added this_cpu_read() and this_cpu_ptr()
fetcharg methods to trace CPU-local data safely
- Fetcharg bytecode dumper: Added CONFIG_PROBE_EVENTS_DUMP_FETCHARG
to dump the compiled fetcharg bytecode instructions as comments in
dynamic_events
- Extended symbol name handling: Removed the MAX_COMMON_HEAD_LEN
limit and extended MAX_ARGSTR_LEN to 256 bytes, enabling probing of
long symbols, mangled Rust symbols and complex BTF expressions
- eprobe variable syntax: Allowed eprobes to reference event fields
directly without requiring a '$' prefix
- Cleanup unused parameters, redundant codes, duplicate macros and
pointer arithmetic
- Use a ternary operator for simplifying fetch_type_from_btf_type()
Expanded boot time dynamic probe support:
- Add boot-time tracing configuration support for event probes
(eprobes), function probes (fprobes), and tracepoint probes
(tprobes)
- Allow comment lines ('#') in dynamic_events file
Optimization, robustness, and cleanups:
- Simplify fprobe_remove_ips() by reusing graph and ftrace helpers
- Remove __packed attribute from struct __fprobe_header to avoid
unaligned memory access penalties on RISC architectures
- Remove redundant memset() calls in perf event probe handlers
- Replace legacy __ASSEMBLY__ with __ASSEMBLER__ in header files
Selftests & refactoring:
- Refactor parse_probe_arg() and parse_probe_vars(), and eliminate
recursion in probe argument parsing to protect kernel stack depth
- Add selftests for BTF typecasts and module probing without module
prefixes
- Force LC_ALL=C in ftracetest to prevent test failures on localized
systems
- Refactor btf_type_skip_modifiers() to remove ignored id parameter
- Sort ERRORS list in trace_probe.h alphabetically
- Fix typo in fprobe docs, and trace_fprobe function name
- Rename FETCH_OP_DATA to FETCH_OP_IMMSTR
- Make file offset error message probe-agnostic"
* tag 'probes-v7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (37 commits)
fprobe: Simplify fprobe_remove_ips() by reusing existing helpers
tracing/boot: Add support for eprobe, fprobe, and tprobe events
selftests/ftrace: Force C locale in ftracetest
tracing/probes: Treating longer symbol name on event comparation
docs: trace: fprobe: fix 'thos' spelling
tracing/probes: Fix extra whitespace in trace_probe_kernel.h
tracing/kprobe: Remove redundant memset in kprobe_perf_func()
tracing/fprobe: Remove redundant memset in fentry_perf_func()
tracing/fprobe: Remove redundant snprintf in trace_fprobe_match_command_head()
tracing/probes: Simplify BTF_KIND_PTR case in fetch_type_from_btf_type()
tracing/probes: Cleanup pointer arithmetic in store_trace_entry_data()
tracing/probes: Remove unused parameter from parse_probe_var_retval()
tracing/probes: Remove redundant bounds check in trace_probe_compare_arg_type()
tracing/probes: Remove redundant boolean conversion in trace_probe_has_single_file()
tracing/probes: Remove duplicate MAX_ARRAY_LEN macro definition
selftests/ftrace: Add test case for a symbol in a module without module name
tracing/probes: Eliminate recursion in parse_probe_arg()
tracing/probes: Extend max length of argument string
tracing/probes: Sort ERRORS list in trace_probe.h alphabetically
tracing/probes: Refactor parse_probe_arg()
...
This commit is contained in:
commit
6439079365
|
|
@ -121,9 +121,21 @@ ftrace.[instance.INSTANCE.]event.synthetic.EVENT.fields = FIELD[, FIELD2[...]]
|
|||
Defines new synthetic event with FIELDs. Each field should be
|
||||
"type varname".
|
||||
|
||||
Note that kprobe and synthetic event definitions can be written under
|
||||
instance node, but those are also visible from other instances. So please
|
||||
take care for event name conflict.
|
||||
ftrace.[instance.INSTANCE.]event.eprobes.EVENT.probes = PROBE[, PROBE2[...]]
|
||||
Defines new event probe based on PROBEs. This option is available only
|
||||
for the event which group name is "eprobes".
|
||||
|
||||
ftrace.[instance.INSTANCE.]event.fprobes.EVENT.probes = PROBE[, PROBE2[...]]
|
||||
Defines new fprobe event based on PROBEs. This option is available only
|
||||
for the event which group name is "fprobes".
|
||||
|
||||
ftrace.[instance.INSTANCE.]event.tracepoints.EVENT.probes = PROBE[, PROBE2[...]]
|
||||
Defines new tracepoint probe based on PROBEs. This option is available only
|
||||
for the event which group name is "tracepoints" or "tprobes".
|
||||
|
||||
Note that dynamic event definitions can be written under instance node, but
|
||||
those are also visible from other instances. So please take care for event
|
||||
name conflict.
|
||||
|
||||
Ftrace Histogram Options
|
||||
------------------------
|
||||
|
|
|
|||
|
|
@ -49,7 +49,12 @@ Synopsis of eprobe_events
|
|||
(STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
|
||||
a pointer to STRUCT and then derference the pointer defined by
|
||||
->MEMBER. Note that when this is used, the FIELD name does not
|
||||
need to be prefixed with a '$'.
|
||||
need to be prefixed with a '$'. ASGN can be specified optionally.
|
||||
If ASGN is specified, FIELD will be cast to the same offset
|
||||
position as the ASGN member, rather than to the beginning of
|
||||
the STRUCT.
|
||||
(STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
|
||||
also be used with another FETCHARG instead of FIELD.
|
||||
|
||||
Types
|
||||
-----
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ will be cancelled.
|
|||
@fregs
|
||||
This is the `ftrace_regs` data structure at the entry and exit. This
|
||||
includes the function parameters, or the return values. So user can
|
||||
access thos values via appropriate `ftrace_regs_*` APIs.
|
||||
access those values via appropriate `ftrace_regs_*` APIs.
|
||||
|
||||
@entry_data
|
||||
This is a local storage to share the data between entry and exit handlers.
|
||||
|
|
|
|||
|
|
@ -50,13 +50,23 @@ Synopsis of fprobe-events
|
|||
$argN : Fetch the Nth function argument. (N >= 1) (\*2)
|
||||
$retval : Fetch return value.(\*3)
|
||||
$comm : Fetch current task comm.
|
||||
$current : Fetch the address of the current task_struct.
|
||||
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*4)(\*5)
|
||||
this_cpu_read(FETCHARG) : Read the value of the per-CPU variable FETCHARG on the current CPU.
|
||||
this_cpu_ptr(FETCHARG) : Get the address of the per-CPU variable FETCHARG on the current CPU.
|
||||
\IMM : Store an immediate value to the argument.
|
||||
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), "char", "string", "ustring", "symbol", "symstr"
|
||||
and bitfield are supported.
|
||||
(STRUCT[,ASGN])FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
|
||||
a pointer to STRUCT and then derference the pointer defined by
|
||||
->MEMBER. ASGN can be specified optionally. If ASGN is specified,
|
||||
FIELD will be cast to the same offset position as the ASGN member,
|
||||
rather than to the beginning of the STRUCT.
|
||||
(STRUCT[,ASGN])(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
|
||||
also be used with another FETCHARG instead of FIELD.
|
||||
|
||||
(\*1) This is available only when BTF is enabled.
|
||||
(\*2) only for the probe on function entry (offs == 0). Note, this argument access
|
||||
|
|
|
|||
|
|
@ -53,7 +53,10 @@ Synopsis of kprobe_events
|
|||
$argN : Fetch the Nth function argument. (N >= 1) (\*1)
|
||||
$retval : Fetch return value.(\*2)
|
||||
$comm : Fetch current task comm.
|
||||
$current : Fetch the address of the current task_struct.
|
||||
+|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4)
|
||||
this_cpu_read(FETCHARG) : Read the value of the per-CPU variable FETCHARG on the current CPU.
|
||||
this_cpu_ptr(FETCHARG) : Get the address of the per-CPU variable FETCHARG on the current CPU.
|
||||
\IMM : Store an immediate value to the argument.
|
||||
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
|
||||
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
|
||||
|
|
@ -61,6 +64,14 @@ Synopsis of kprobe_events
|
|||
(x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
|
||||
"string", "ustring", "symbol", "symstr" and bitfield are
|
||||
supported.
|
||||
(STRUCT[,ASGN])FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
|
||||
a pointer to STRUCT and then derference the pointer defined by
|
||||
->MEMBER. Note that this is available only when the probe is
|
||||
on function entry. ASGN can be specified optionally. If ASGN
|
||||
is specified, FIELD will be cast to the same offset position
|
||||
as the ASGN member, rather than to the beginning of the STRUCT.
|
||||
(STRUCT[,ASGN])(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
|
||||
also be used with another FETCHARG instead of FIELD.
|
||||
|
||||
(\*1) only for the probe on function entry (offs == 0). Note, this argument access
|
||||
is best effort, because depending on the argument type, it may be passed on
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#ifndef _ASM_GENERIC_KPROBES_H
|
||||
#define _ASM_GENERIC_KPROBES_H
|
||||
|
||||
#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
|
||||
#if defined(__KERNEL__) && !defined(__ASSEMBLER__)
|
||||
#ifdef CONFIG_KPROBES
|
||||
/*
|
||||
* Blacklist ganerating macro. Specify functions which is not probed
|
||||
|
|
@ -21,6 +21,6 @@ static unsigned long __used \
|
|||
# define __kprobes
|
||||
# define nokprobe_inline inline
|
||||
#endif
|
||||
#endif /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */
|
||||
#endif /* defined(__KERNEL__) && !defined(__ASSEMBLER__) */
|
||||
|
||||
#endif /* _ASM_GENERIC_KPROBES_H */
|
||||
|
|
|
|||
|
|
@ -779,6 +779,18 @@ config PROBE_EVENTS_BTF_ARGS
|
|||
kernel function entry or a tracepoint.
|
||||
This is available only if BTF (BPF Type Format) support is enabled.
|
||||
|
||||
config PROBE_EVENTS_DUMP_FETCHARG
|
||||
bool "Dump of dynamic probe event fetch-arguments"
|
||||
depends on PROBE_EVENTS
|
||||
default n
|
||||
help
|
||||
This shows the dump of fetch-arguments of dynamic probe events
|
||||
alongside their event definitions in the dynamic_events file
|
||||
as comment lines. This is useful to debug the probe events.
|
||||
Since this exposes the raw values in the dynamic_events file,
|
||||
it might be a security risk. Only enable it if you need to debug
|
||||
probe events themselves.
|
||||
|
||||
config KPROBE_EVENTS
|
||||
depends on KPROBES
|
||||
depends on HAVE_REGS_AND_STACK_ACCESS_API
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ static inline void read_fprobe_header(unsigned long *stack,
|
|||
struct __fprobe_header {
|
||||
struct fprobe *fp;
|
||||
unsigned long size_words;
|
||||
} __packed;
|
||||
};
|
||||
|
||||
#define FPROBE_HEADER_SIZE_IN_LONG SIZE_IN_LONG(sizeof(struct __fprobe_header))
|
||||
|
||||
|
|
@ -464,15 +464,8 @@ static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace)
|
|||
#ifdef CONFIG_MODULES
|
||||
static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt)
|
||||
{
|
||||
if (!nr_fgraph_fprobes)
|
||||
__fprobe_graph_unregister();
|
||||
else if (cnt)
|
||||
ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0);
|
||||
|
||||
if (!nr_ftrace_fprobes)
|
||||
__fprobe_ftrace_unregister();
|
||||
else if (cnt)
|
||||
ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0);
|
||||
fprobe_graph_remove_ips(ips, cnt);
|
||||
fprobe_ftrace_remove_ips(ips, cnt);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -4320,14 +4320,16 @@ static const char readme_msg[] =
|
|||
"\t args: <name>=fetcharg[:type]\n"
|
||||
"\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n"
|
||||
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
|
||||
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
|
||||
"\t $stack<index>, $stack, $retval, $comm, $arg<N>, $current\n"
|
||||
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
|
||||
"\t <argname>[->field[->field|.field...]],\n"
|
||||
"\t [(structname[,field])]<argname>[->field[->field|.field...]],\n"
|
||||
"\t [(structname[,field])](fetcharg)->field[->field|.field...],\n"
|
||||
#endif
|
||||
#else
|
||||
"\t $stack<index>, $stack, $retval, $comm,\n"
|
||||
"\t $stack<index>, $stack, $retval, $comm, $current\n"
|
||||
#endif
|
||||
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
|
||||
"\t this_cpu_read(<fetcharg>), this_cpu_ptr(<fetcharg>)\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 b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/trace_events.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_dynevent.h"
|
||||
|
||||
#define MAX_BUF_LEN 256
|
||||
|
||||
|
|
@ -172,6 +173,93 @@ trace_boot_add_synth_event(struct xbc_node *node, const char *event)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_EPROBE_EVENTS) || defined(CONFIG_FPROBE_EVENTS)
|
||||
static int __init
|
||||
trace_boot_add_probe_event(struct xbc_node *node, const char *group,
|
||||
const char *event, char type, const char *type_name)
|
||||
{
|
||||
struct xbc_node *anode;
|
||||
char buf[MAX_BUF_LEN];
|
||||
const char *val;
|
||||
int ret = 0;
|
||||
|
||||
xbc_node_for_each_array_value(node, "probes", anode, val) {
|
||||
if (val[0] == type && (val[1] == ':' || isspace(val[1]))) {
|
||||
ret = strscpy(buf, val, MAX_BUF_LEN);
|
||||
if (ret < 0) {
|
||||
pr_err("%s command is too long: %s\n", type_name, val);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = snprintf(buf, MAX_BUF_LEN, "%c:%s/%s %s", type, group, event, val);
|
||||
if (ret >= MAX_BUF_LEN || ret < 0) {
|
||||
pr_err("%s command is too long: %c:%s/%s %s\n",
|
||||
type_name, type, group, event, val);
|
||||
ret = -E2BIG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dyn_event_create(buf, NULL);
|
||||
if (ret) {
|
||||
pr_err("Failed to add %s: %s\n", type_name, buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_EPROBE_EVENTS
|
||||
static inline int __init
|
||||
trace_boot_add_eprobe_event(struct xbc_node *node, const char *group,
|
||||
const char *event)
|
||||
{
|
||||
return trace_boot_add_probe_event(node, group, event, 'e', "eprobe");
|
||||
}
|
||||
#else
|
||||
static inline int __init
|
||||
trace_boot_add_eprobe_event(struct xbc_node *node, const char *group,
|
||||
const char *event)
|
||||
{
|
||||
pr_err("Event probe is not supported.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FPROBE_EVENTS
|
||||
static inline int __init
|
||||
trace_boot_add_fprobe_event(struct xbc_node *node, const char *group,
|
||||
const char *event)
|
||||
{
|
||||
return trace_boot_add_probe_event(node, group, event, 'f', "fprobe");
|
||||
}
|
||||
|
||||
static inline int __init
|
||||
trace_boot_add_tprobe_event(struct xbc_node *node, const char *group,
|
||||
const char *event)
|
||||
{
|
||||
return trace_boot_add_probe_event(node, group, event, 't', "tprobe");
|
||||
}
|
||||
#else
|
||||
static inline int __init
|
||||
trace_boot_add_fprobe_event(struct xbc_node *node, const char *group,
|
||||
const char *event)
|
||||
{
|
||||
pr_err("Fprobe event is not supported.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int __init
|
||||
trace_boot_add_tprobe_event(struct xbc_node *node, const char *group,
|
||||
const char *event)
|
||||
{
|
||||
pr_err("Tracepoint probe is not supported.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HIST_TRIGGERS
|
||||
static int __init __printf(3, 4)
|
||||
append_printf(char **bufp, char *end, const char *fmt, ...)
|
||||
|
|
@ -477,6 +565,15 @@ trace_boot_init_one_event(struct trace_array *tr, struct xbc_node *gnode,
|
|||
if (!strcmp(group, "synthetic"))
|
||||
if (trace_boot_add_synth_event(enode, event) < 0)
|
||||
return;
|
||||
if (!strcmp(group, "eprobes"))
|
||||
if (trace_boot_add_eprobe_event(enode, group, event) < 0)
|
||||
return;
|
||||
if (!strcmp(group, "fprobes"))
|
||||
if (trace_boot_add_fprobe_event(enode, group, event) < 0)
|
||||
return;
|
||||
if (!strcmp(group, "tracepoints") || !strcmp(group, "tprobes"))
|
||||
if (trace_boot_add_tprobe_event(enode, group, event) < 0)
|
||||
return;
|
||||
|
||||
mutex_lock(&event_mutex);
|
||||
file = find_event_file(tr, group, event);
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *type
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int create_dyn_event(const char *raw_command);
|
||||
|
||||
/*
|
||||
* Locked version of event creation. The event creation must be protected by
|
||||
* dyn_event_ops_mutex because of protecting trace_probe_log.
|
||||
|
|
@ -124,6 +126,9 @@ int dyn_event_create(const char *raw_command, struct dyn_event_operations *type)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (!type)
|
||||
return create_dyn_event(raw_command);
|
||||
|
||||
mutex_lock(&dyn_event_ops_mutex);
|
||||
ret = type->create(raw_command);
|
||||
mutex_unlock(&dyn_event_ops_mutex);
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ static int eprobe_dyn_event_show(struct seq_file *m, struct dyn_event *ev)
|
|||
seq_printf(m, " %s=%s", ep->tp.args[i].name, ep->tp.args[i].comm);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
trace_probe_dump_args(m, &ep->tp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,13 +238,10 @@ static bool trace_fprobe_is_busy(struct dyn_event *ev)
|
|||
static bool trace_fprobe_match_command_head(struct trace_fprobe *tf,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
char buf[MAX_ARGSTR_LEN + 1];
|
||||
|
||||
if (!argc)
|
||||
return true;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", trace_fprobe_symbol(tf));
|
||||
if (strcmp(buf, argv[0]))
|
||||
if (strcmp(trace_fprobe_symbol(tf), argv[0]))
|
||||
return false;
|
||||
argc--; argv++;
|
||||
|
||||
|
|
@ -474,7 +471,6 @@ static int fentry_perf_func(struct trace_fprobe *tf, unsigned long entry_ip,
|
|||
regs = ftrace_fill_perf_regs(fregs, regs);
|
||||
|
||||
entry->ip = entry_ip;
|
||||
memset(&entry[1], 0, dsize);
|
||||
store_trace_args(&entry[1], &tf->tp, fregs, NULL, sizeof(*entry), dsize);
|
||||
perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs,
|
||||
head, NULL);
|
||||
|
|
@ -764,7 +760,7 @@ static int unregister_fprobe_event(struct trace_fprobe *tf)
|
|||
return trace_probe_unregister_event_call(&tf->tp);
|
||||
}
|
||||
|
||||
static int __regsiter_tracepoint_fprobe(struct trace_fprobe *tf)
|
||||
static int __register_tracepoint_fprobe(struct trace_fprobe *tf)
|
||||
{
|
||||
struct tracepoint_user *tuser __free(tuser_put) = NULL;
|
||||
struct module *mod __free(module_put) = NULL;
|
||||
|
|
@ -836,7 +832,7 @@ static int __register_trace_fprobe(struct trace_fprobe *tf)
|
|||
tf->fp.flags &= ~FPROBE_FL_DISABLED;
|
||||
|
||||
if (trace_fprobe_is_tracepoint(tf))
|
||||
return __regsiter_tracepoint_fprobe(tf);
|
||||
return __register_tracepoint_fprobe(tf);
|
||||
|
||||
/* TODO: handle filter, nofilter or symbol list */
|
||||
return register_fprobe(&tf->fp, tf->symbol, NULL);
|
||||
|
|
@ -1449,6 +1445,8 @@ static int trace_fprobe_show(struct seq_file *m, struct dyn_event *ev)
|
|||
seq_printf(m, " %s=%s", tf->tp.args[i].name, tf->tp.args[i].comm);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
trace_probe_dump_args(m, &tf->tp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -149,20 +149,28 @@ static bool trace_kprobe_is_busy(struct dyn_event *ev)
|
|||
static bool trace_kprobe_match_command_head(struct trace_kprobe *tk,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
char buf[MAX_ARGSTR_LEN + 1];
|
||||
char buf[32];
|
||||
int len;
|
||||
|
||||
if (!argc)
|
||||
return true;
|
||||
|
||||
if (!tk->symbol)
|
||||
if (!tk->symbol) {
|
||||
snprintf(buf, sizeof(buf), "0x%p", tk->rp.kp.addr);
|
||||
else if (tk->rp.kp.offset)
|
||||
snprintf(buf, sizeof(buf), "%s+%u",
|
||||
trace_kprobe_symbol(tk), tk->rp.kp.offset);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "%s", trace_kprobe_symbol(tk));
|
||||
if (strcmp(buf, argv[0]))
|
||||
if (strcmp(buf, argv[0]))
|
||||
return false;
|
||||
} else if (tk->rp.kp.offset) {
|
||||
len = strlen(trace_kprobe_symbol(tk));
|
||||
if (strncmp(trace_kprobe_symbol(tk), argv[0], len) ||
|
||||
argv[0][len] != '+')
|
||||
return false;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%u", tk->rp.kp.offset);
|
||||
if (strcmp(buf, &argv[0][len + 1]))
|
||||
return false;
|
||||
} else if (strcmp(trace_kprobe_symbol(tk), argv[0]))
|
||||
return false;
|
||||
|
||||
argc--; argv++;
|
||||
|
||||
return trace_probe_match_command_args(&tk->tp, argc, argv);
|
||||
|
|
@ -1320,6 +1328,8 @@ static int trace_kprobe_show(struct seq_file *m, struct dyn_event *ev)
|
|||
seq_printf(m, " %s=%s", tk->tp.args[i].name, tk->tp.args[i].comm);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
trace_probe_dump_args(m, &tk->tp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1719,7 +1729,6 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)
|
|||
return 0;
|
||||
|
||||
entry->ip = (unsigned long)tk->rp.kp.addr;
|
||||
memset(&entry[1], 0, 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,
|
||||
head, NULL);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -32,7 +32,7 @@
|
|||
#include "trace_output.h"
|
||||
|
||||
#define MAX_TRACE_ARGS 128
|
||||
#define MAX_ARGSTR_LEN 63
|
||||
#define MAX_ARGSTR_LEN 255
|
||||
#define MAX_ARRAY_LEN 64
|
||||
#define MAX_ARG_NAME_LEN 32
|
||||
#define MAX_BTF_ARGS_LEN 128
|
||||
|
|
@ -83,38 +83,48 @@ static nokprobe_inline u32 update_data_loc(u32 loc, int consumed)
|
|||
/* Printing function type */
|
||||
typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
|
||||
|
||||
enum fetch_op {
|
||||
FETCH_OP_NOP = 0,
|
||||
// Stage 1 (load) ops
|
||||
FETCH_OP_REG, /* Register : .param = offset */
|
||||
FETCH_OP_STACK, /* Stack : .param = index */
|
||||
FETCH_OP_STACKP, /* Stack pointer */
|
||||
FETCH_OP_RETVAL, /* Return value */
|
||||
FETCH_OP_IMM, /* Immediate : .immediate */
|
||||
FETCH_OP_COMM, /* Current comm */
|
||||
FETCH_OP_ARG, /* Function argument : .param */
|
||||
FETCH_OP_FOFFS, /* File offset: .immediate */
|
||||
FETCH_OP_DATA, /* Allocated data: .data */
|
||||
FETCH_OP_EDATA, /* Entry data: .offset */
|
||||
// Stage 2 (dereference) op
|
||||
FETCH_OP_DEREF, /* Dereference: .offset */
|
||||
FETCH_OP_UDEREF, /* User-space Dereference: .offset */
|
||||
// Stage 3 (store) ops
|
||||
FETCH_OP_ST_RAW, /* Raw: .size */
|
||||
FETCH_OP_ST_MEM, /* Mem: .offset, .size */
|
||||
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 */
|
||||
FETCH_OP_ST_EDATA, /* Store Entry Data: .offset */
|
||||
// Stage 4 (modify) op
|
||||
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
|
||||
// Stage 5 (loop) op
|
||||
FETCH_OP_LP_ARRAY, /* Array: .param = loop count */
|
||||
FETCH_OP_TP_ARG, /* Trace Point argument */
|
||||
FETCH_OP_END,
|
||||
FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */
|
||||
};
|
||||
#define FETCH_OP_LIST { \
|
||||
/* Stage 1 (load) ops */ \
|
||||
FETCH_OP(NOP, none), /* NOP */ \
|
||||
FETCH_OP(REG, param), /* Register: .param = offset */ \
|
||||
FETCH_OP(STACK, param), /* Stack: .param = index */ \
|
||||
FETCH_OP(STACKP, none), /* Stack pointer */ \
|
||||
FETCH_OP(RETVAL, none), /* Return value */ \
|
||||
FETCH_OP(IMM, imm), /* Immediate: .immediate */ \
|
||||
FETCH_OP(COMM, none), /* Current comm */ \
|
||||
FETCH_OP(CURRENT, none), /* Current task_struct address */\
|
||||
FETCH_OP(ARG, param), /* Argument: .param = index */ \
|
||||
FETCH_OP(FOFFS, imm), /* File offset: .immediate */ \
|
||||
FETCH_OP(IMMSTR, string), /* Allocated string: .data */ \
|
||||
FETCH_OP(EDATA, offset), /* Entry data: .offset */ \
|
||||
FETCH_OP(TP_ARG, tp_arg), /* Tracepoint argument: .data */\
|
||||
/* Stage 2 (dereference) ops */ \
|
||||
FETCH_OP(DEREF, offset), /* Dereference: .offset */ \
|
||||
FETCH_OP(UDEREF, offset), /* User-space dereference: .offset */\
|
||||
FETCH_OP(CPU_PTR, none), /* Per-CPU pointer: .offset */ \
|
||||
/* Stage 3 (store) ops */ \
|
||||
FETCH_OP(ST_RAW, store), /* Raw value: .size */ \
|
||||
FETCH_OP(ST_MEM, store), /* Memory: .offset, .size */ \
|
||||
FETCH_OP(ST_UMEM, store), /* User memory: .offset, .size */\
|
||||
FETCH_OP(ST_STRING, store), /* String: .offset, .size */ \
|
||||
FETCH_OP(ST_USTRING, store), /* User string: .offset, .size */\
|
||||
FETCH_OP(ST_SYMSTR, store), /* Symbol name: .offset, .size */\
|
||||
FETCH_OP(ST_EDATA, offset), /* Entry data: .offset */ \
|
||||
/* Stage 4 (modify) op */ \
|
||||
FETCH_OP(MOD_BF, bf), /* Bitfield: .basesize, .lshift, .rshift*/\
|
||||
/* Stage 5 (loop) op */ \
|
||||
FETCH_OP(LP_ARRAY, param), /* Loop array: .param = count */\
|
||||
/* End */ \
|
||||
FETCH_OP(END, none), \
|
||||
/* Unresolved Symbol holder */ \
|
||||
FETCH_OP(NOP_SYMBOL, symbol), /* Non loaded symbol: .data = symbol name */\
|
||||
}
|
||||
|
||||
#define FETCH_OP(opname, decode_fn) FETCH_OP_##opname
|
||||
enum fetch_op FETCH_OP_LIST;
|
||||
#undef FETCH_OP
|
||||
|
||||
#define FETCH_NOP_SYMBOL FETCH_OP_NOP_SYMBOL
|
||||
|
||||
struct fetch_insn {
|
||||
enum fetch_op op;
|
||||
|
|
@ -209,7 +219,6 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
|
|||
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)
|
||||
|
||||
#define ASSIGN_FETCH_TYPE_END {}
|
||||
#define MAX_ARRAY_LEN 64
|
||||
|
||||
#ifdef CONFIG_KPROBE_EVENTS
|
||||
bool trace_kprobe_on_func_entry(struct trace_event_call *call);
|
||||
|
|
@ -350,7 +359,7 @@ static inline int trace_probe_unregister_event_call(struct trace_probe *tp)
|
|||
|
||||
static inline bool trace_probe_has_single_file(struct trace_probe *tp)
|
||||
{
|
||||
return !!list_is_singular(&tp->event->files);
|
||||
return list_is_singular(&tp->event->files);
|
||||
}
|
||||
|
||||
int trace_probe_init(struct trace_probe *tp, const char *event,
|
||||
|
|
@ -370,6 +379,13 @@ bool trace_probe_match_command_args(struct trace_probe *tp,
|
|||
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);
|
||||
#ifdef CONFIG_PROBE_EVENTS_DUMP_FETCHARG
|
||||
void trace_probe_dump_args(struct seq_file *m, struct trace_probe *tp);
|
||||
#else
|
||||
static inline void trace_probe_dump_args(struct seq_file *m, struct trace_probe *tp)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
|
||||
int traceprobe_get_entry_data_size(struct trace_probe *tp);
|
||||
|
|
@ -414,6 +430,39 @@ static inline bool tparg_is_function_return(unsigned int flags)
|
|||
return (flags & TPARG_FL_LOC_MASK) == (TPARG_FL_KERNEL | TPARG_FL_RETURN);
|
||||
}
|
||||
|
||||
static inline bool tparg_is_event_probe(unsigned int flags)
|
||||
{
|
||||
return !!(flags & TPARG_FL_TEVENT);
|
||||
}
|
||||
|
||||
/* Each typecast consumes nested level. So the max number of typecast is 8. */
|
||||
#define TRACEPROBE_MAX_NESTED_LEVEL 8
|
||||
|
||||
enum parse_state_type {
|
||||
STATE_DEREF,
|
||||
STATE_TYPECAST,
|
||||
};
|
||||
|
||||
struct parse_state {
|
||||
int type;
|
||||
union {
|
||||
struct {
|
||||
int deref;
|
||||
long offset;
|
||||
int cur_offs;
|
||||
char *inner_arg;
|
||||
bool is_cpu_read;
|
||||
} deref;
|
||||
struct {
|
||||
char *casttype;
|
||||
char *fieldname;
|
||||
int orig_offset;
|
||||
int field_offset_diff;
|
||||
char *inner_arg;
|
||||
} typecast;
|
||||
};
|
||||
};
|
||||
|
||||
struct traceprobe_parse_context {
|
||||
struct trace_event_call *event;
|
||||
/* BTF related parameters */
|
||||
|
|
@ -430,8 +479,12 @@ struct traceprobe_parse_context {
|
|||
struct trace_probe *tp;
|
||||
unsigned int flags;
|
||||
int offset;
|
||||
int prefix_byteoffs; /* The byte offset of the prefix field of typecast */
|
||||
struct parse_state stack[TRACEPROBE_MAX_NESTED_LEVEL + 1];
|
||||
int depth;
|
||||
};
|
||||
|
||||
|
||||
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
|
||||
const char *argv,
|
||||
struct traceprobe_parse_context *ctx);
|
||||
|
|
@ -485,88 +538,95 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
|
|||
|
||||
#undef ERRORS
|
||||
#define ERRORS \
|
||||
C(FILE_NOT_FOUND, "Failed to find the given file"), \
|
||||
C(NO_REGULAR_FILE, "Not a regular file"), \
|
||||
C(BAD_REFCNT, "Invalid reference counter offset"), \
|
||||
C(REFCNT_OPEN_BRACE, "Reference counter brace is not closed"), \
|
||||
C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \
|
||||
C(BAD_UPROBE_OFFS, "Invalid uprobe offset"), \
|
||||
C(BAD_MAXACT_TYPE, "Maxactive is only for function exit"), \
|
||||
C(BAD_MAXACT, "Invalid maxactive number"), \
|
||||
C(MAXACT_TOO_BIG, "Maxactive is too big"), \
|
||||
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
|
||||
C(NON_UNIQ_SYMBOL, "The symbol is not unique"), \
|
||||
C(BAD_RETPROBE, "Retprobe address must be an function entry"), \
|
||||
C(NO_TRACEPOINT, "Tracepoint is not found"), \
|
||||
C(BAD_TP_NAME, "Invalid character in tracepoint name"),\
|
||||
C(BAD_ADDR_SUFFIX, "Invalid probed address suffix"), \
|
||||
C(NO_GROUP_NAME, "Group name is not specified"), \
|
||||
C(GROUP_TOO_LONG, "Group name is too long"), \
|
||||
C(BAD_GROUP_NAME, "Group name must follow the same rules as C identifiers"), \
|
||||
C(NO_EVENT_NAME, "Event name is not specified"), \
|
||||
C(EVENT_TOO_LONG, "Event name is too long"), \
|
||||
C(BAD_EVENT_NAME, "Event name must follow the same rules as C identifiers"), \
|
||||
C(EVENT_EXIST, "Given group/event name is already used by another event"), \
|
||||
C(RETVAL_ON_PROBE, "$retval is not available on probe"), \
|
||||
C(NO_RETVAL, "This function returns 'void' type"), \
|
||||
C(BAD_STACK_NUM, "Invalid stack number"), \
|
||||
C(BAD_ARG_NUM, "Invalid argument number"), \
|
||||
C(BAD_VAR, "Invalid $-variable specified"), \
|
||||
C(BAD_REG_NAME, "Invalid register name"), \
|
||||
C(BAD_MEM_ADDR, "Invalid memory address"), \
|
||||
C(BAD_IMM, "Invalid immediate value"), \
|
||||
C(IMMSTR_NO_CLOSE, "String is not closed with '\"'"), \
|
||||
C(FILE_ON_KPROBE, "File offset is not available with kprobe"), \
|
||||
C(BAD_FILE_OFFS, "Invalid file offset value"), \
|
||||
C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \
|
||||
C(TOO_MANY_OPS, "Dereference is too much nested"), \
|
||||
C(DEREF_NEED_BRACE, "Dereference needs a brace"), \
|
||||
C(BAD_DEREF_OFFS, "Invalid dereference offset"), \
|
||||
C(DEREF_OPEN_BRACE, "Dereference brace is not closed"), \
|
||||
C(COMM_CANT_DEREF, "$comm can not be dereferenced"), \
|
||||
C(BAD_FETCH_ARG, "Invalid fetch argument"), \
|
||||
C(ARRAY_NO_CLOSE, "Array is not closed"), \
|
||||
C(BAD_ARRAY_SUFFIX, "Array has wrong suffix"), \
|
||||
C(BAD_ARRAY_NUM, "Invalid array size"), \
|
||||
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"), \
|
||||
C(BAD_ARG_NAME, "Argument name must follow the same rules as C identifiers"), \
|
||||
C(USED_ARG_NAME, "This argument name is already used"), \
|
||||
C(ARG_TOO_LONG, "Argument expression is too long"), \
|
||||
C(NO_ARG_BODY, "No argument expression"), \
|
||||
C(BAD_INSN_BNDRY, "Probe point is not an instruction boundary"),\
|
||||
C(FAIL_REG_PROBE, "Failed to register probe event"),\
|
||||
C(DIFF_PROBE_TYPE, "Probe type is different from existing probe"),\
|
||||
C(DIFF_ARG_TYPE, "Argument type or name is different from existing probe"),\
|
||||
C(SAME_PROBE, "There is already the exact same probe event"),\
|
||||
C(NO_EVENT_INFO, "This requires both group and event name to attach"),\
|
||||
C(BAD_ATTACH_EVENT, "Attached event does not exist"),\
|
||||
C(BAD_ATTACH_ARG, "Attached event does not have this field"),\
|
||||
C(NO_EP_FILTER, "No filter rule after 'if'"), \
|
||||
C(NOSUP_BTFARG, "BTF is not available or not supported"), \
|
||||
C(NO_BTFARG, "This variable is not found at 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(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(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(ARGS_2LONG, "$arg* failed because the argument list is too long"), \
|
||||
C(ARG_NAME_TOO_LONG, "Argument name is too long"), \
|
||||
C(ARG_TOO_LONG, "Argument expression is too long"), \
|
||||
C(ARRAY_NO_CLOSE, "Array is not closed"), \
|
||||
C(ARRAY_TOO_BIG, "Array number is too big"), \
|
||||
C(BAD_ADDR_SUFFIX, "Invalid probed address suffix"), \
|
||||
C(BAD_ARG_NAME, "Argument name must follow the same rules as C identifiers"), \
|
||||
C(BAD_ARG_NUM, "Invalid argument number"), \
|
||||
C(BAD_ARRAY_NUM, "Invalid array size"), \
|
||||
C(BAD_ARRAY_SUFFIX, "Array has wrong suffix"), \
|
||||
C(BAD_ATTACH_ARG, "Attached event does not have this field"), \
|
||||
C(BAD_ATTACH_EVENT, "Attached event does not exist"), \
|
||||
C(BAD_BITFIELD, "Invalid bitfield"), \
|
||||
C(BAD_BTF_TID, "Failed to get BTF type info."), \
|
||||
C(BAD_DEREF_OFFS, "Invalid dereference offset"), \
|
||||
C(BAD_EVENT_NAME, "Event name must follow the same rules as C identifiers"), \
|
||||
C(BAD_FETCH_ARG, "Invalid fetch argument"), \
|
||||
C(BAD_FILE_OFFS, "Invalid file offset value"), \
|
||||
C(BAD_GROUP_NAME, "Group name must follow the same rules as C identifiers"), \
|
||||
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."),\
|
||||
C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"),\
|
||||
C(TOO_MANY_ARGS, "Too many arguments are specified"), \
|
||||
C(BAD_IMM, "Invalid immediate value"), \
|
||||
C(BAD_INSN_BNDRY, "Probe point is not an instruction boundary"), \
|
||||
C(BAD_MAXACT, "Invalid maxactive number"), \
|
||||
C(BAD_MAXACT_TYPE, "Maxactive is only for function exit"), \
|
||||
C(BAD_MEM_ADDR, "Invalid memory address"), \
|
||||
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
|
||||
C(BAD_REFCNT, "Invalid reference counter offset"), \
|
||||
C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \
|
||||
C(BAD_REG_NAME, "Invalid register name"), \
|
||||
C(BAD_RETPROBE, "Retprobe address must be an function entry"), \
|
||||
C(BAD_STACK_NUM, "Invalid stack number"), \
|
||||
C(BAD_STRING, "String accepts only memory argument"), \
|
||||
C(BAD_SYMSTRING, "Symbol String doesn't accept data/userdata"), \
|
||||
C(BAD_TP_NAME, "Invalid character in tracepoint name"), \
|
||||
C(BAD_TYPE, "Unknown type is specified"), \
|
||||
C(BAD_TYPE4STR, "This type does not fit for string."), \
|
||||
C(BAD_UPROBE_OFFS, "Invalid uprobe offset"), \
|
||||
C(BAD_VAR, "Invalid $-variable specified"), \
|
||||
C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."), \
|
||||
C(COMM_CANT_DEREF, "$comm can not be dereferenced"), \
|
||||
C(DEREF_NEED_BRACE, "Dereference needs a brace"), \
|
||||
C(DEREF_OPEN_BRACE, "Dereference brace is not closed"), \
|
||||
C(DIFF_ARG_TYPE, "Argument type or name is different from existing probe"), \
|
||||
C(DIFF_PROBE_TYPE, "Probe type is different from existing probe"), \
|
||||
C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \
|
||||
C(EVENT_EXIST, "Given group/event name is already used by another event"), \
|
||||
C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \
|
||||
C(EVENT_TOO_LONG, "Event name is too long"), \
|
||||
C(FAIL_REG_PROBE, "Failed to register probe event"), \
|
||||
C(FILE_NOT_FOUND, "Failed to find the given file"), \
|
||||
C(FILE_ON_KPROBE, "File offset is not available for kernel probes"), \
|
||||
C(GROUP_TOO_LONG, "Group name is too long"), \
|
||||
C(IMMSTR_NO_CLOSE, "String is not closed with '\"'"), \
|
||||
C(MAXACT_TOO_BIG, "Maxactive is too big"), \
|
||||
C(NEED_STRING_TYPE, "$comm and immediate-string only accepts string type"), \
|
||||
C(NOFENTRY_ARGS, "$arg* can be used only on function entry or exit"), \
|
||||
C(NON_UNIQ_SYMBOL, "The symbol is not unique"), \
|
||||
C(NOSUP_BTFARG, "BTF is not available or not supported"), \
|
||||
C(NOSUP_DAT_ARG, "Non pointer structure/union argument is not supported."), \
|
||||
C(NOSUP_PERCPU, "Per-cpu variable access is only for kernel probes"), \
|
||||
C(NO_ARG_BODY, "No argument expression"), \
|
||||
C(NO_ARG_NAME, "Argument name is not specified"), \
|
||||
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_FIELD, "This field is not found."), \
|
||||
C(NO_EP_FILTER, "No filter rule after 'if'"), \
|
||||
C(NO_EVENT_FIELD, "This event field is not found."), \
|
||||
C(NO_EVENT_INFO, "This requires both group and event name to attach"), \
|
||||
C(NO_EVENT_NAME, "Event name is not specified"), \
|
||||
C(NO_GROUP_NAME, "Group name is not specified"), \
|
||||
C(NO_PTR_STRCT, "This is not a pointer to union/structure."), \
|
||||
C(NO_REGULAR_FILE, "Not a regular file"), \
|
||||
C(NO_RETVAL, "This function returns 'void' type"), \
|
||||
C(NO_TRACEPOINT, "Tracepoint is not found"), \
|
||||
C(REFCNT_OPEN_BRACE, "Reference counter brace is not closed"), \
|
||||
C(RETVAL_ON_PROBE, "$retval is not available on probe"), \
|
||||
C(SAME_PROBE, "There is already the exact same probe event"), \
|
||||
C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \
|
||||
C(TOO_MANY_ARGS, "Too many arguments are specified"), \
|
||||
C(TOO_MANY_EARGS, "Too many entry arguments specified"), \
|
||||
C(EVENT_TOO_BIG, "Event too big (too many fields?)"), \
|
||||
C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"),
|
||||
C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"), \
|
||||
C(TOO_MANY_OPS, "Dereference is too much nested"), \
|
||||
C(TYPECAST_BAD_ARROW, "Typecast field option does not support -> operator"), \
|
||||
C(TYPECAST_NOT_ALIGNED, "Typecast field option is not byte-aligned"), \
|
||||
C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \
|
||||
C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \
|
||||
C(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses"), \
|
||||
C(USED_ARG_NAME, "This argument name is already used"),
|
||||
|
||||
#undef C
|
||||
#define C(a, b) TP_ERR_##a
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
static nokprobe_inline int
|
||||
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;
|
||||
|
||||
return strnlen_user_nofault(uaddr, MAX_STRING_SIZE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,9 +109,12 @@ process_common_fetch_insn(struct fetch_insn *code, unsigned long *val)
|
|||
case FETCH_OP_COMM:
|
||||
*val = (unsigned long)current->comm;
|
||||
break;
|
||||
case FETCH_OP_DATA:
|
||||
case FETCH_OP_IMMSTR:
|
||||
*val = (unsigned long)code->data;
|
||||
break;
|
||||
case FETCH_OP_CURRENT:
|
||||
*val = (unsigned long)current;
|
||||
break;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
|
@ -126,25 +129,35 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
|
|||
struct fetch_insn *s3 = NULL;
|
||||
int total = 0, ret = 0, i = 0;
|
||||
u32 loc = 0;
|
||||
unsigned long lval = val;
|
||||
unsigned long lval, llval = val;
|
||||
|
||||
stage2:
|
||||
/* 2nd stage: dereference memory if needed */
|
||||
do {
|
||||
if (code->op == FETCH_OP_DEREF) {
|
||||
lval = val;
|
||||
lval = val;
|
||||
switch (code->op) {
|
||||
case FETCH_OP_DEREF:
|
||||
ret = probe_mem_read(&val, (void *)val + code->offset,
|
||||
sizeof(val));
|
||||
} else if (code->op == FETCH_OP_UDEREF) {
|
||||
lval = val;
|
||||
break;
|
||||
case FETCH_OP_UDEREF:
|
||||
ret = probe_mem_read_user(&val,
|
||||
(void *)val + code->offset, sizeof(val));
|
||||
} else
|
||||
break;
|
||||
case FETCH_OP_CPU_PTR:
|
||||
val = (unsigned long)this_cpu_ptr((void __percpu *)val);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
lval = llval;
|
||||
goto out;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
llval = lval;
|
||||
code++;
|
||||
} while (1);
|
||||
out:
|
||||
|
||||
s3 = code;
|
||||
stage3:
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ static bool trace_uprobe_is_busy(struct dyn_event *ev)
|
|||
static bool trace_uprobe_match_command_head(struct trace_uprobe *tu,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
char buf[MAX_ARGSTR_LEN + 1];
|
||||
char buf[64];
|
||||
int len;
|
||||
|
||||
if (!argc)
|
||||
|
|
@ -765,6 +765,9 @@ static int trace_uprobe_show(struct seq_file *m, struct dyn_event *ev)
|
|||
seq_printf(m, " %s=%s", tu->tp.args[i].name, tu->tp.args[i].comm);
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
trace_probe_dump_args(m, &tu->tp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,20 @@ static int simple_thread_fn(void *arg)
|
|||
static DEFINE_MUTEX(thread_mutex);
|
||||
static int simple_thread_cnt;
|
||||
|
||||
static struct foo_timer_data *foo_timer_data;
|
||||
|
||||
static void sample_timer_cb(struct timer_list *t)
|
||||
{
|
||||
struct foo_timer_data *data = container_of(t, struct foo_timer_data, timer);
|
||||
|
||||
get_cpu();
|
||||
trace_foo_timer_fn(data);
|
||||
(*this_cpu_ptr(data->counter))++;
|
||||
put_cpu();
|
||||
|
||||
mod_timer(t, jiffies + HZ);
|
||||
}
|
||||
|
||||
int foo_bar_reg(void)
|
||||
{
|
||||
mutex_lock(&thread_mutex);
|
||||
|
|
@ -132,9 +146,27 @@ void foo_bar_unreg(void)
|
|||
|
||||
static int __init trace_event_init(void)
|
||||
{
|
||||
foo_timer_data = kzalloc_obj(*foo_timer_data, GFP_KERNEL);
|
||||
if (!foo_timer_data)
|
||||
return -ENOMEM;
|
||||
|
||||
foo_timer_data->name = "sample_timer_counter";
|
||||
foo_timer_data->counter = alloc_percpu(int);
|
||||
if (!foo_timer_data->counter) {
|
||||
kfree(foo_timer_data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
timer_setup(&foo_timer_data->timer, sample_timer_cb, 0);
|
||||
mod_timer(&foo_timer_data->timer, jiffies + HZ);
|
||||
|
||||
simple_tsk = kthread_run(simple_thread, NULL, "event-sample");
|
||||
if (IS_ERR(simple_tsk))
|
||||
return -1;
|
||||
if (IS_ERR(simple_tsk)) {
|
||||
timer_shutdown_sync(&foo_timer_data->timer);
|
||||
free_percpu(foo_timer_data->counter);
|
||||
kfree(foo_timer_data);
|
||||
return PTR_ERR(simple_tsk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -147,6 +179,10 @@ static void __exit trace_event_exit(void)
|
|||
kthread_stop(simple_tsk_fn);
|
||||
simple_tsk_fn = NULL;
|
||||
mutex_unlock(&thread_mutex);
|
||||
|
||||
timer_shutdown_sync(&foo_timer_data->timer);
|
||||
free_percpu(foo_timer_data->counter);
|
||||
kfree(foo_timer_data);
|
||||
}
|
||||
|
||||
module_init(trace_event_init);
|
||||
|
|
|
|||
|
|
@ -247,12 +247,14 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* It is OK to have helper functions in the file, but they need to be protected
|
||||
* from being defined more than once. Remember, this file gets included more
|
||||
* than once.
|
||||
* It is OK to have helper functions and data structures in the file, but they
|
||||
* need to be protected from being defined more than once. Remember, this file
|
||||
* gets included more than once.
|
||||
*/
|
||||
#ifndef __TRACE_EVENT_SAMPLE_HELPER_FUNCTIONS
|
||||
#define __TRACE_EVENT_SAMPLE_HELPER_FUNCTIONS
|
||||
#include <linux/timer.h>
|
||||
|
||||
static inline int __length_of(const int *list)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -270,6 +272,13 @@ enum {
|
|||
TRACE_SAMPLE_BAR = 4,
|
||||
TRACE_SAMPLE_ZOO = 8,
|
||||
};
|
||||
|
||||
struct foo_timer_data {
|
||||
const char *name;
|
||||
struct timer_list timer;
|
||||
int __percpu *counter;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -595,6 +604,25 @@ TRACE_EVENT(foo_rel_loc,
|
|||
__get_rel_bitmask(bitmask),
|
||||
__get_rel_cpumask(cpumask))
|
||||
);
|
||||
|
||||
TRACE_EVENT(foo_timer_fn,
|
||||
|
||||
TP_PROTO(struct foo_timer_data *data),
|
||||
|
||||
TP_ARGS(data),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string( name, data->name )
|
||||
__field( int, count )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(name);
|
||||
__entry->count = *this_cpu_ptr(data->counter);
|
||||
),
|
||||
|
||||
TP_printk("name=%s count=%d", __get_str(name), __entry->count)
|
||||
);
|
||||
#endif
|
||||
|
||||
/***** NOTICE! The #if protection ends here. *****/
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ EOF
|
|||
kprobe_event_options() {
|
||||
cat $TRACEFS/kprobe_events | while read p args; do
|
||||
case $p in
|
||||
\#*)
|
||||
continue;;
|
||||
r*)
|
||||
cat 1>&2 << EOF
|
||||
# WARN: A return probe found but it is not supported by bootconfig. Skip it.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
# Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
|
||||
#
|
||||
|
||||
# Keep command output parsing stable regardless of the user's locale.
|
||||
export LC_ALL=C
|
||||
|
||||
usage() { # errno [message]
|
||||
[ ! -z "$2" ] && echo $2
|
||||
echo "Usage: ftracetest [options] [testcase(s)] [testcase-directory(s)]"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# description: BTF event with typecast and percpu access
|
||||
# requires: dynamic_events "this_cpu_read(<fetcharg>)":README "[(structname[,field])]<argname>[->field[->field|.field...]]":README
|
||||
|
||||
# Check if the sample module is loaded
|
||||
if ! lsmod | grep -q trace_events_sample; then
|
||||
modprobe trace-events-sample || exit_unresolved
|
||||
fi
|
||||
|
||||
echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
# The sample_timer_cb(struct timer_list *t) is called.
|
||||
# We want to check (STRUCT,FIELD)VAR typecast and this_cpu_read() access.
|
||||
# (foo_timer_data,timer)t converts t to struct foo_timer_data * using container_of.
|
||||
# data->counter is a per-cpu pointer to int.
|
||||
# this_cpu_read(data->counter) should give the value of the counter.
|
||||
|
||||
echo 'f:mysample/myevent sample_timer_cb name=(foo_timer_data,timer)t->name:string count=this_cpu_read((foo_timer_data,timer)t->counter)' >> dynamic_events
|
||||
|
||||
echo 1 > events/mysample/myevent/enable
|
||||
echo 1 > events/sample-trace/foo_timer_fn/enable
|
||||
|
||||
sleep 2
|
||||
|
||||
echo 0 > events/mysample/myevent/enable
|
||||
echo 0 > events/sample-trace/foo_timer_fn/enable
|
||||
|
||||
# Compare the values.
|
||||
MATCH=0
|
||||
while read line; do
|
||||
if echo $line | grep -q "foo_timer_fn:"; then
|
||||
NAME=`echo $line | sed 's/.*name=\([^ ]*\) .*/\1/'`
|
||||
COUNT=`echo $line | sed 's/.*count=\([^ ]*\).*/\1/'`
|
||||
if grep -q "myevent:.*name=\"${NAME}\" count=$COUNT" trace; then
|
||||
MATCH=$((MATCH+1))
|
||||
fi
|
||||
fi
|
||||
done < trace
|
||||
|
||||
if [ $MATCH -eq 0 ]; then
|
||||
echo "No matching events found"
|
||||
exit_fail
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
echo 0 > events/mysample/myevent/enable
|
||||
echo 0 > events/sample-trace/foo_timer_fn/enable
|
||||
echo > dynamic_events
|
||||
clear_trace
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# description: BTF typecast and percpu access syntax validation
|
||||
# requires: dynamic_events "this_cpu_read(<fetcharg>)":README "[(structname[,field])]<argname>[->field[->field|.field...]]":README
|
||||
|
||||
KPROBES=
|
||||
FPROBES=
|
||||
|
||||
if grep -qF "p[:[<group>/][<event>]] <place> [<args>]" README ; then
|
||||
KPROBES=yes
|
||||
fi
|
||||
if grep -qF "f[:[<group>/][<event>]] <func-name>[%return] [<args>]" README ; then
|
||||
FPROBES=yes
|
||||
fi
|
||||
|
||||
if [ -z "$KPROBES" -a -z "$FPROBES" ] ; then
|
||||
exit_unsupported
|
||||
fi
|
||||
|
||||
echo 0 > events/enable
|
||||
echo > dynamic_events
|
||||
|
||||
# Load trace-events-sample module if available to have per-CPU counter structure defined
|
||||
if ! lsmod | grep -q trace_events_sample; then
|
||||
modprobe trace-events-sample || exit_unresolved
|
||||
fi
|
||||
|
||||
if [ "$FPROBES" ] ; then
|
||||
# 1. Test basic typecast on fprobe
|
||||
echo 'f:fpevent1 vfs_read name=(file)file->f_path.dentry->d_name.name:string' >> dynamic_events
|
||||
# 2. Test parenthesized typecast target on fprobe
|
||||
echo 'f:fpevent2 vfs_read name=(file)(file)->f_path.dentry->d_name.name:string' >> dynamic_events
|
||||
# 3. Test nested typecasts on fprobe
|
||||
echo 'f:fpevent3 vfs_read name=(dentry)((file)file->f_path.dentry)->d_name.name:string' >> dynamic_events
|
||||
# 4. Test container_of-style typecast with field option on fprobe
|
||||
echo 'f:fpevent4 vfs_read name=(file,f_path)file->f_mode' >> dynamic_events
|
||||
# 5. Test typecast on return value on fprobe
|
||||
echo 'f:fpevent5 vfs_read%return name=(file)$retval->f_path.dentry->d_name.name:string' >> dynamic_events
|
||||
# 6. Test $current variable support on fprobe
|
||||
echo 'f:fpevent6 vfs_read pid=$current->pid' >> dynamic_events
|
||||
echo 'f:fpevent7 vfs_read pid=(task_struct)$current->pid' >> dynamic_events
|
||||
echo 'f:fpevent8 vfs_read pid=(task_struct,group_leader)$current->pid' >> dynamic_events
|
||||
|
||||
# Test this_cpu_read and this_cpu_ptr on fprobe
|
||||
echo 'f:fpevent9 sample_timer_cb name=(foo_timer_data,timer)t->name:string count=this_cpu_read((foo_timer_data,timer)t->counter)' >> dynamic_events
|
||||
echo 'f:fpevent10 sample_timer_cb ptr=this_cpu_ptr((foo_timer_data,timer)t->counter)' >> dynamic_events
|
||||
fi
|
||||
|
||||
if [ "$KPROBES" ] ; then
|
||||
# 7. Test basic typecast on kprobe
|
||||
echo 'p:kpevent1 vfs_read name=(file)file->f_path.dentry->d_name.name:string' >> dynamic_events
|
||||
# 8. Test parenthesized typecast target on kprobe
|
||||
echo 'p:kpevent2 vfs_read name=(file)(file)->f_path.dentry->d_name.name:string' >> dynamic_events
|
||||
# 9. Test nested typecasts on kprobe
|
||||
echo 'p:kpevent3 vfs_read name=(dentry)((file)file->f_path.dentry)->d_name.name:string' >> dynamic_events
|
||||
# 10. Test container_of-style typecast with field option on kprobe
|
||||
echo 'p:kpevent4 vfs_read name=(file,f_path)file->f_mode' >> dynamic_events
|
||||
# 11. Test typecast on return value on kretprobe
|
||||
echo 'r:kpevent5 vfs_read name=(file)$retval->f_path.dentry->d_name.name:string' >> dynamic_events
|
||||
# 12. Test $current variable support on kprobe
|
||||
echo 'p:kpevent6 vfs_read pid=$current->pid' >> dynamic_events
|
||||
echo 'p:kpevent7 vfs_read pid=(task_struct)$current->pid' >> dynamic_events
|
||||
echo 'p:kpevent8 vfs_read pid=(task_struct,group_leader)$current->pid' >> dynamic_events
|
||||
|
||||
# Test this_cpu_read and this_cpu_ptr on kprobe
|
||||
echo 'p:kpevent9 sample_timer_cb name=(foo_timer_data,timer)t->name:string count=this_cpu_read((foo_timer_data,timer)t->counter)' >> dynamic_events
|
||||
echo 'p:kpevent10 sample_timer_cb ptr=this_cpu_ptr((foo_timer_data,timer)t->counter)' >> dynamic_events
|
||||
fi
|
||||
|
||||
# Verify the events exist in dynamic_events
|
||||
if [ "$FPROBES" ] ; then
|
||||
grep -q "fpevent1 " dynamic_events
|
||||
grep -q "fpevent2 " dynamic_events
|
||||
grep -q "fpevent3 " dynamic_events
|
||||
grep -q "fpevent4 " dynamic_events
|
||||
grep -q "fpevent5 " dynamic_events
|
||||
grep -q "fpevent6 " dynamic_events
|
||||
grep -q "fpevent7 " dynamic_events
|
||||
grep -q "fpevent8 " dynamic_events
|
||||
if lsmod | grep -q trace_events_sample; then
|
||||
grep -q "fpevent9 " dynamic_events
|
||||
grep -q "fpevent10 " dynamic_events
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$KPROBES" ] ; then
|
||||
grep -q "kpevent1 " dynamic_events
|
||||
grep -q "kpevent2 " dynamic_events
|
||||
grep -q "kpevent3 " dynamic_events
|
||||
grep -q "kpevent4 " dynamic_events
|
||||
grep -q "kpevent5 " dynamic_events
|
||||
grep -q "kpevent6 " dynamic_events
|
||||
grep -q "kpevent7 " dynamic_events
|
||||
grep -q "kpevent8 " dynamic_events
|
||||
if lsmod | grep -q trace_events_sample; then
|
||||
grep -q "kpevent9 " dynamic_events
|
||||
grep -q "kpevent10 " dynamic_events
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
echo > dynamic_events
|
||||
clear_trace
|
||||
|
|
@ -10,7 +10,7 @@ check_error() { # command-with-error-pos-by-^
|
|||
check_error 'e ^a.' # NO_EVENT_INFO
|
||||
check_error 'e ^.b' # NO_EVENT_INFO
|
||||
check_error 'e ^a.b' # BAD_ATTACH_EVENT
|
||||
check_error 'e syscalls/sys_enter_openat ^foo' # BAD_ATTACH_ARG
|
||||
check_error 'e syscalls/sys_enter_openat ^foo' # NO_EVENT_FIELD
|
||||
check_error 'e:^/bar syscalls/sys_enter_openat' # NO_GROUP_NAME
|
||||
check_error 'e:^12345678901234567890123456789012345678901234567890123456789012345/bar syscalls/sys_enter_openat' # GROUP_TOO_LONG
|
||||
|
||||
|
|
@ -19,11 +19,19 @@ check_error 'e:^ syscalls/sys_enter_openat' # NO_EVENT_NAME
|
|||
check_error 'e:foo/^12345678901234567890123456789012345678901234567890123456789012345 syscalls/sys_enter_openat' # EVENT_TOO_LONG
|
||||
check_error 'e:foo/^bar.1 syscalls/sys_enter_openat' # BAD_EVENT_NAME
|
||||
|
||||
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^dfd' # BAD_FETCH_ARG
|
||||
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^$foo' # BAD_ATTACH_ARG
|
||||
|
||||
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^COMM' # NO_EVENT_FIELD
|
||||
if grep -q "\$current.*" README; then
|
||||
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^current' # NO_EVENT_FIELD
|
||||
fi
|
||||
|
||||
if grep -q '<attached-group>\.<attached-event>.*\[if <filter>\]' README; then
|
||||
check_error 'e:foo/bar syscalls/sys_enter_openat if ^' # NO_EP_FILTER
|
||||
fi
|
||||
|
||||
if grep -q 'this_cpu_read(<fetcharg>)' README; then
|
||||
check_error 'e:foo/bar syscalls/sys_enter_openat arg=^this_cpu_read(file)' # NOSUP_PERCPU
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ check_error 'f vfs_read ^&1' # BAD_FETCH_ARG
|
|||
|
||||
# We've introduced this limitation with array support
|
||||
if grep -q ' <type>\\\[<array-size>\\\]' README; then
|
||||
check_error 'f vfs_read +0(^+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(@0))))))))))))))' # TOO_MANY_OPS?
|
||||
check_error 'f vfs_read +0(+0(+0(+0(+0(+0(+0(+0(^+0(@0)))))))))' # TOO_MANY_NESTED
|
||||
check_error 'f vfs_read +0(@11):u8[10^' # ARRAY_NO_CLOSE
|
||||
check_error 'f vfs_read +0(@11):u8[10]^a' # BAD_ARRAY_SUFFIX
|
||||
check_error 'f vfs_read +0(@11):u8[^10a]' # BAD_ARRAY_NUM
|
||||
|
|
@ -75,7 +75,7 @@ check_error 'f vfs_read ^arg123456789012345678901234567890=@11' # ARG_NAME_TOO_L
|
|||
check_error 'f vfs_read ^=@11' # NO_ARG_NAME
|
||||
check_error 'f vfs_read ^var.1=@11' # BAD_ARG_NAME
|
||||
check_error 'f vfs_read var1=@11 ^var1=@12' # USED_ARG_NAME
|
||||
check_error 'f vfs_read ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))' # ARG_TOO_LONG
|
||||
check_error 'f vfs_read ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))))))))))))))))))))))' # ARG_TOO_LONG
|
||||
check_error 'f vfs_read arg1=^' # NO_ARG_BODY
|
||||
|
||||
|
||||
|
|
@ -112,6 +112,18 @@ 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
|
||||
if grep -qF "[(structname" README ; then
|
||||
check_error 'f vfs_read arg1=(task_struct)file^' # TYPECAST_REQ_FIELD
|
||||
check_error 'f vfs_read arg1=(a)((b)((c)((d)((e)((f)((g)((h)(^(i)file->i)->h)->g)->f)->e)->d)->c)->b)->a' # TOO_MANY_NESTED
|
||||
check_error 'f vfs_read arg1=(task_struct,^in_execve)file->comm' # TYPECAST_NOT_ALIGNED
|
||||
check_error 'f vfs_read arg1=(task_struct,^foo_bar)file->pid' # NO_BTF_FIELD
|
||||
check_error 'f vfs_read arg1=(^task_struct1234)file->pid' # NO_PTR_STRCT
|
||||
check_error 'f vfs_read arg1=(task_struct,se^->group_node)file->comm' # TYPECAST_BAD_ARROW
|
||||
check_error 'f vfs_read arg1=(task_struct,^->pid)file->comm' # NO_BTF_FIELD
|
||||
check_error 'f vfs_read arg1=(task_struct,^.pid)file->comm' # NO_BTF_FIELD
|
||||
check_error 'f vfs_read arg1=(task_struct,^.)file->comm' # NO_BTF_FIELD
|
||||
check_error 'f vfs_read arg1=(task_struct)^@symbol+10->comm' # TYPECAST_SYM_OFFSET
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ check_error 't kfree ^&1' # BAD_FETCH_ARG
|
|||
|
||||
# We've introduced this limitation with array support
|
||||
if grep -q ' <type>\\\[<array-size>\\\]' README; then
|
||||
check_error 't kfree +0(^+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(@0))))))))))))))' # TOO_MANY_OPS?
|
||||
check_error 't kfree +0(+0(+0(+0(+0(+0(+0(+0(^+0(@0)))))))))' # TOO_MANY_NESTED
|
||||
check_error 't kfree +0(@11):u8[10^' # ARRAY_NO_CLOSE
|
||||
check_error 't kfree +0(@11):u8[10]^a' # BAD_ARRAY_SUFFIX
|
||||
check_error 't kfree +0(@11):u8[^10a]' # BAD_ARRAY_NUM
|
||||
|
|
@ -61,7 +61,7 @@ check_error 't kfree ^arg123456789012345678901234567890=@11' # ARG_NAME_TOO_LOG
|
|||
check_error 't kfree ^=@11' # NO_ARG_NAME
|
||||
check_error 't kfree ^var.1=@11' # BAD_ARG_NAME
|
||||
check_error 't kfree var1=@11 ^var1=@12' # USED_ARG_NAME
|
||||
check_error 't kfree ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))' # ARG_TOO_LONG
|
||||
check_error 't kfree ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))))))))))))))))))))))' # ARG_TOO_LONG
|
||||
check_error 't kfree arg1=^' # NO_ARG_BODY
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ fi
|
|||
MOD=trace_printk
|
||||
FUNC=trace_printk_irq_work
|
||||
|
||||
:;: "Add an event on a module function without module name" ;:
|
||||
|
||||
echo "p:event0 $FUNC" > kprobe_events
|
||||
test -d events/kprobes/event0 || exit_failure
|
||||
echo "-:kprobes/event0" >> kprobe_events
|
||||
|
||||
:;: "Add an event on a module function without specifying event name" ;:
|
||||
|
||||
echo "p $MOD:$FUNC" > kprobe_events
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ check_error 'p vfs_read ^&1' # BAD_FETCH_ARG
|
|||
|
||||
# We've introduced this limitation with array support
|
||||
if grep -q ' <type>\\\[<array-size>\\\]' README; then
|
||||
check_error 'p vfs_read +0(^+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(@0))))))))))))))' # TOO_MANY_OPS?
|
||||
check_error 'p vfs_read +0(+0(+0(+0(+0(+0(+0(+0(^+0(@0)))))))))' # TOO_MANY_NESTED
|
||||
check_error 'p vfs_read +0(@11):u8[10^' # ARRAY_NO_CLOSE
|
||||
check_error 'p vfs_read +0(@11):u8[10]^a' # BAD_ARRAY_SUFFIX
|
||||
check_error 'p vfs_read +0(@11):u8[^10a]' # BAD_ARRAY_NUM
|
||||
|
|
@ -71,7 +71,7 @@ check_error 'p vfs_read ^arg123456789012345678901234567890=@11' # ARG_NAME_TOO_L
|
|||
check_error 'p vfs_read ^=@11' # NO_ARG_NAME
|
||||
check_error 'p vfs_read ^var.1=@11' # BAD_ARG_NAME
|
||||
check_error 'p vfs_read var1=@11 ^var1=@12' # USED_ARG_NAME
|
||||
check_error 'p vfs_read ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))' # ARG_TOO_LONG
|
||||
check_error 'p vfs_read ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))))))))))))))))))))))' # ARG_TOO_LONG
|
||||
check_error 'p vfs_read arg1=^' # NO_ARG_BODY
|
||||
|
||||
# instruction boundary check is valid on x86 (at this moment)
|
||||
|
|
@ -115,6 +115,18 @@ check_error 'p vfs_read+20 ^$arg*' # NOFENTRY_ARGS
|
|||
check_error 'p vfs_read ^hoge' # NO_BTFARG
|
||||
check_error 'p kfree ^$arg10' # NO_BTFARG (exceed the number of parameters)
|
||||
check_error 'r kfree ^$retval' # NO_RETVAL
|
||||
if grep -qF "[(structname" README ; then
|
||||
check_error 'p vfs_read arg1=(task_struct)file^' # TYPECAST_REQ_FIELD
|
||||
check_error 'p vfs_read arg1=(a)((b)((c)((d)((e)((f)((g)((h)(^(i)file->i)->h)->g)->f)->e)->d)->c)->b)->a' # TOO_MANY_NESTED
|
||||
check_error 'p vfs_read arg1=(task_struct,^in_execve)file->comm' # TYPECAST_NOT_ALIGNED
|
||||
check_error 'p vfs_read arg1=(task_struct,^foo_bar)file->pid' # NO_BTF_FIELD
|
||||
check_error 'p vfs_read arg1=(^task_struct1234)file->pid' # NO_PTR_STRCT
|
||||
check_error 'p vfs_read arg1=(task_struct,se^->group_node)file->comm' # TYPECAST_BAD_ARROW
|
||||
check_error 'p vfs_read arg1=(task_struct,^->pid)file->comm' # NO_BTF_FIELD
|
||||
check_error 'p vfs_read arg1=(task_struct,^.pid)file->comm' # NO_BTF_FIELD
|
||||
check_error 'p vfs_read arg1=(task_struct,^.)file->comm' # NO_BTF_FIELD
|
||||
check_error 'p vfs_read arg1=(task_struct)^@symbol+10->comm' # TYPECAST_SYM_OFFSET
|
||||
fi
|
||||
else
|
||||
check_error 'p vfs_read ^$arg*' # NOSUP_BTFARG
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -28,4 +28,9 @@ if grep -q ".*symstr.*" README; then
|
|||
check_error 'p /bin/sh:10 $stack0:^symstr' # BAD_TYPE
|
||||
fi
|
||||
|
||||
# $current is not supported by uprobe
|
||||
if grep -q "\$current.*" README; then
|
||||
check_error 'p /bin/sh:10 ^$current:u8' # BAD_VAR
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user