Commit 7491e2c4 authored by Tzvetomir Stoyanov (VMware)'s avatar Tzvetomir Stoyanov (VMware) Committed by Steven Rostedt (VMware)

tracing: Add a probe that attaches to trace events

A new dynamic event is introduced: event probe. The event is attached
to an existing tracepoint and uses its fields as arguments. The user
can specify custom format string of the new event, select what tracepoint
arguments will be printed and how to print them.
An event probe is created by writing configuration string in
'dynamic_events' ftrace file:
 e[:[SNAME/]ENAME] SYSTEM/EVENT [FETCHARGS]	- Set an event probe
 -:SNAME/ENAME					- Delete an event probe

Where:
 SNAME	- System name, if omitted 'eprobes' is used.
 ENAME	- Name of the new event in SNAME, if omitted the SYSTEM_EVENT is used.
 SYSTEM	- Name of the system, where the tracepoint is defined, mandatory.
 EVENT	- Name of the tracepoint event in SYSTEM, mandatory.
 FETCHARGS - Arguments:
  <name>=$<field>[:TYPE] - Fetch given filed of the tracepoint and print
			   it as given TYPE with given name. Supported
			   types are:
	                    (u8/u16/u32/u64/s8/s16/s32/s64), basic type
        	            (x8/x16/x32/x64), hexadecimal types
			    "string", "ustring" and bitfield.

Example, attach an event probe on openat system call and print name of the
file that will be opened:
 echo "e:esys/eopen syscalls/sys_enter_openat file=\$filename:string" >> dynamic_events
A new dynamic event is created in events/esys/eopen/ directory. It
can be deleted with:
 echo "-:esys/eopen" >> dynamic_events

Filters, triggers and histograms can be attached to the new event, it can
be matched in synthetic events. There is one limitation - an event probe
can not be attached to kprobe, uprobe or another event probe.

Link: https://lkml.kernel.org/r/20210812145805.2292326-1-tz.stoyanov@gmail.com
Link: https://lkml.kernel.org/r/20210819152825.142428383@goodmis.orgAcked-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Co-developed-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: default avatarTzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 8e242060
...@@ -313,6 +313,7 @@ enum { ...@@ -313,6 +313,7 @@ enum {
TRACE_EVENT_FL_DYNAMIC_BIT, TRACE_EVENT_FL_DYNAMIC_BIT,
TRACE_EVENT_FL_KPROBE_BIT, TRACE_EVENT_FL_KPROBE_BIT,
TRACE_EVENT_FL_UPROBE_BIT, TRACE_EVENT_FL_UPROBE_BIT,
TRACE_EVENT_FL_EPROBE_BIT,
}; };
/* /*
...@@ -325,6 +326,7 @@ enum { ...@@ -325,6 +326,7 @@ enum {
* DYNAMIC - Event is a dynamic event (created at run time) * DYNAMIC - Event is a dynamic event (created at run time)
* KPROBE - Event is a kprobe * KPROBE - Event is a kprobe
* UPROBE - Event is a uprobe * UPROBE - Event is a uprobe
* EPROBE - Event is an event probe
*/ */
enum { enum {
TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT),
...@@ -335,6 +337,7 @@ enum { ...@@ -335,6 +337,7 @@ enum {
TRACE_EVENT_FL_DYNAMIC = (1 << TRACE_EVENT_FL_DYNAMIC_BIT), TRACE_EVENT_FL_DYNAMIC = (1 << TRACE_EVENT_FL_DYNAMIC_BIT),
TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT), TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT),
TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT), TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT),
TRACE_EVENT_FL_EPROBE = (1 << TRACE_EVENT_FL_EPROBE_BIT),
}; };
#define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE) #define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE)
...@@ -680,6 +683,7 @@ enum event_trigger_type { ...@@ -680,6 +683,7 @@ enum event_trigger_type {
ETT_EVENT_ENABLE = (1 << 3), ETT_EVENT_ENABLE = (1 << 3),
ETT_EVENT_HIST = (1 << 4), ETT_EVENT_HIST = (1 << 4),
ETT_HIST_ENABLE = (1 << 5), ETT_HIST_ENABLE = (1 << 5),
ETT_EVENT_EPROBE = (1 << 6),
}; };
extern int filter_match_preds(struct event_filter *filter, void *rec); extern int filter_match_preds(struct event_filter *filter, void *rec);
......
...@@ -77,6 +77,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o ...@@ -77,6 +77,7 @@ obj-$(CONFIG_EVENT_TRACING) += trace_event_perf.o
endif endif
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o obj-$(CONFIG_EVENT_TRACING) += trace_events_trigger.o
obj-$(CONFIG_PROBE_EVENTS) += trace_eprobe.o
obj-$(CONFIG_TRACE_EVENT_INJECT) += trace_events_inject.o obj-$(CONFIG_TRACE_EVENT_INJECT) += trace_events_inject.o
obj-$(CONFIG_SYNTH_EVENTS) += trace_events_synth.o obj-$(CONFIG_SYNTH_EVENTS) += trace_events_synth.o
obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o obj-$(CONFIG_HIST_TRIGGERS) += trace_events_hist.o
......
...@@ -5543,6 +5543,7 @@ static const char readme_msg[] = ...@@ -5543,6 +5543,7 @@ static const char readme_msg[] =
#ifdef CONFIG_HIST_TRIGGERS #ifdef CONFIG_HIST_TRIGGERS
"\t s:[synthetic/]<event> <field> [<field>]\n" "\t s:[synthetic/]<event> <field> [<field>]\n"
#endif #endif
"\t e[:[<group>/]<event>] <attached-group>.<attached-event> [<args>]\n"
"\t -:[<group>/]<event>\n" "\t -:[<group>/]<event>\n"
#ifdef CONFIG_KPROBE_EVENTS #ifdef CONFIG_KPROBE_EVENTS
"\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n" "\t place: [<module>:]<symbol>[+<offset>]|<memaddr>\n"
...@@ -5552,7 +5553,7 @@ static const char readme_msg[] = ...@@ -5552,7 +5553,7 @@ static const char readme_msg[] =
" place (uprobe): <path>:<offset>[%return][(ref_ctr_offset)]\n" " place (uprobe): <path>:<offset>[%return][(ref_ctr_offset)]\n"
#endif #endif
"\t args: <name>=fetcharg[:type]\n" "\t args: <name>=fetcharg[:type]\n"
"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n" "\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n"
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n" "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
#else #else
...@@ -5567,6 +5568,8 @@ static const char readme_msg[] = ...@@ -5567,6 +5568,8 @@ static const char readme_msg[] =
"\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" "\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n"
"\t [unsigned] char/int/long\n" "\t [unsigned] char/int/long\n"
#endif #endif
"\t efield: For event probes ('e' types), the field is on of the fields\n"
"\t of the <attached-group>/<attached-event>.\n"
#endif #endif
" events/\t\t- Directory containing all trace event subsystems:\n" " events/\t\t- Directory containing all trace event subsystems:\n"
" enable\t\t- Write 0/1 to enable/disable tracing of all events\n" " enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
......
...@@ -126,6 +126,11 @@ struct kprobe_trace_entry_head { ...@@ -126,6 +126,11 @@ struct kprobe_trace_entry_head {
unsigned long ip; unsigned long ip;
}; };
struct eprobe_trace_entry_head {
struct trace_entry ent;
unsigned int type;
};
struct kretprobe_trace_entry_head { struct kretprobe_trace_entry_head {
struct trace_entry ent; struct trace_entry ent;
unsigned long func; unsigned long func;
...@@ -1508,9 +1513,14 @@ static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; } ...@@ -1508,9 +1513,14 @@ static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; }
extern int register_trigger_cmds(void); extern int register_trigger_cmds(void);
extern void clear_event_triggers(struct trace_array *tr); extern void clear_event_triggers(struct trace_array *tr);
enum {
EVENT_TRIGGER_FL_PROBE = BIT(0),
};
struct event_trigger_data { struct event_trigger_data {
unsigned long count; unsigned long count;
int ref; int ref;
int flags;
struct event_trigger_ops *ops; struct event_trigger_ops *ops;
struct event_command *cmd_ops; struct event_command *cmd_ops;
struct event_filter __rcu *filter; struct event_filter __rcu *filter;
...@@ -1918,6 +1928,14 @@ static inline bool is_good_name(const char *name) ...@@ -1918,6 +1928,14 @@ static inline bool is_good_name(const char *name)
return true; return true;
} }
/* Convert certain expected symbols into '_' when generating event names */
static inline void sanitize_event_name(char *name)
{
while (*name++ != '\0')
if (*name == ':' || *name == '.')
*name = '_';
}
/* /*
* This is a generic way to read and write a u64 value from a file in tracefs. * This is a generic way to read and write a u64 value from a file in tracefs.
* *
......
This diff is collapsed.
...@@ -124,6 +124,18 @@ static void *trigger_next(struct seq_file *m, void *t, loff_t *pos) ...@@ -124,6 +124,18 @@ static void *trigger_next(struct seq_file *m, void *t, loff_t *pos)
return seq_list_next(t, &event_file->triggers, pos); return seq_list_next(t, &event_file->triggers, pos);
} }
static bool check_user_trigger(struct trace_event_file *file)
{
struct event_trigger_data *data;
list_for_each_entry_rcu(data, &file->triggers, list) {
if (data->flags & EVENT_TRIGGER_FL_PROBE)
continue;
return true;
}
return false;
}
static void *trigger_start(struct seq_file *m, loff_t *pos) static void *trigger_start(struct seq_file *m, loff_t *pos)
{ {
struct trace_event_file *event_file; struct trace_event_file *event_file;
...@@ -134,7 +146,7 @@ static void *trigger_start(struct seq_file *m, loff_t *pos) ...@@ -134,7 +146,7 @@ static void *trigger_start(struct seq_file *m, loff_t *pos)
if (unlikely(!event_file)) if (unlikely(!event_file))
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
if (list_empty(&event_file->triggers)) if (list_empty(&event_file->triggers) || !check_user_trigger(event_file))
return *pos == 0 ? SHOW_AVAILABLE_TRIGGERS : NULL; return *pos == 0 ? SHOW_AVAILABLE_TRIGGERS : NULL;
return seq_list_start(&event_file->triggers, *pos); return seq_list_start(&event_file->triggers, *pos);
......
...@@ -707,14 +707,6 @@ static struct notifier_block trace_kprobe_module_nb = { ...@@ -707,14 +707,6 @@ static struct notifier_block trace_kprobe_module_nb = {
.priority = 1 /* Invoked after kprobe module callback */ .priority = 1 /* Invoked after kprobe module callback */
}; };
/* Convert certain expected symbols into '_' when generating event names */
static inline void sanitize_event_name(char *name)
{
while (*name++ != '\0')
if (*name == ':' || *name == '.')
*name = '_';
}
static int __trace_kprobe_create(int argc, const char *argv[]) static int __trace_kprobe_create(int argc, const char *argv[])
{ {
/* /*
......
...@@ -319,6 +319,13 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, ...@@ -319,6 +319,13 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
code->op = FETCH_OP_ARG; code->op = FETCH_OP_ARG;
code->param = (unsigned int)param - 1; code->param = (unsigned int)param - 1;
#endif #endif
} else if (flags & TPARG_FL_TPOINT) {
if (code->data)
return -EFAULT;
code->data = kstrdup(arg, GFP_KERNEL);
if (!code->data)
return -ENOMEM;
code->op = FETCH_OP_TP_ARG;
} else } else
goto inval_var; goto inval_var;
...@@ -646,13 +653,14 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, ...@@ -646,13 +653,14 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
!strcmp(parg->type->name, "ustring")) { !strcmp(parg->type->name, "ustring")) {
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF && if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM && code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
code->op != FETCH_OP_DATA) { code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
trace_probe_log_err(offset + (t ? (t - arg) : 0), trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING); BAD_STRING);
goto fail; goto fail;
} }
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM || if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
code->op == FETCH_OP_DATA) || parg->count) { code->op == FETCH_OP_DATA) || code->op == FETCH_OP_TP_ARG ||
parg->count) {
/* /*
* IMM, DATA and COMM is pointing actual address, those * IMM, DATA and COMM is pointing actual address, those
* must be kept, and if parg->count != 0, this is an * must be kept, and if parg->count != 0, this is an
...@@ -867,6 +875,10 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, ...@@ -867,6 +875,10 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
fmt = "(%lx <- %lx)"; fmt = "(%lx <- %lx)";
arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP; arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP;
break; break;
case PROBE_PRINT_EVENT:
fmt = "(%u)";
arg = "REC->" FIELD_STRING_TYPE;
break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
return 0; return 0;
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#define FIELD_STRING_IP "__probe_ip" #define FIELD_STRING_IP "__probe_ip"
#define FIELD_STRING_RETIP "__probe_ret_ip" #define FIELD_STRING_RETIP "__probe_ret_ip"
#define FIELD_STRING_FUNC "__probe_func" #define FIELD_STRING_FUNC "__probe_func"
#define FIELD_STRING_TYPE "__probe_type"
#undef DEFINE_FIELD #undef DEFINE_FIELD
#define DEFINE_FIELD(type, item, name, is_signed) \ #define DEFINE_FIELD(type, item, name, is_signed) \
...@@ -102,6 +103,7 @@ enum fetch_op { ...@@ -102,6 +103,7 @@ enum fetch_op {
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
// Stage 5 (loop) op // Stage 5 (loop) op
FETCH_OP_LP_ARRAY, /* Array: .param = loop count */ FETCH_OP_LP_ARRAY, /* Array: .param = loop count */
FETCH_OP_TP_ARG, /* Trace Point argument */
FETCH_OP_END, FETCH_OP_END,
FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */ FETCH_NOP_SYMBOL, /* Unresolved Symbol holder */
}; };
...@@ -351,7 +353,8 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char ...@@ -351,7 +353,8 @@ int trace_probe_create(const char *raw_command, int (*createfn)(int, const char
#define TPARG_FL_RETURN BIT(0) #define TPARG_FL_RETURN BIT(0)
#define TPARG_FL_KERNEL BIT(1) #define TPARG_FL_KERNEL BIT(1)
#define TPARG_FL_FENTRY BIT(2) #define TPARG_FL_FENTRY BIT(2)
#define TPARG_FL_MASK GENMASK(2, 0) #define TPARG_FL_TPOINT BIT(3)
#define TPARG_FL_MASK GENMASK(3, 0)
extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, extern int traceprobe_parse_probe_arg(struct trace_probe *tp, int i,
const char *argv, unsigned int flags); const char *argv, unsigned int flags);
...@@ -366,6 +369,7 @@ int traceprobe_parse_event_name(const char **pevent, const char **pgroup, ...@@ -366,6 +369,7 @@ int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
enum probe_print_type { enum probe_print_type {
PROBE_PRINT_NORMAL, PROBE_PRINT_NORMAL,
PROBE_PRINT_RETURN, PROBE_PRINT_RETURN,
PROBE_PRINT_EVENT,
}; };
extern int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptype); extern int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptype);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment