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:
Linus Torvalds 2026-08-20 15:11:52 -07:00
commit 6439079365
31 changed files with 1542 additions and 507 deletions

View File

@ -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
------------------------

View File

@ -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
-----

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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);

View File

@ -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. *****/

View File

@ -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.

View File

@ -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)]"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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