Merge tag 'trace-v6.14-3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing updates from Steven Rostedt:

 - Cleanup with guard() and free() helpers

   There were several places in the code that had a lot of "goto out" in
   the error paths to either unlock a lock or free some memory that was
   allocated. But this is error prone. Convert the code over to use the
   guard() and free() helpers that let the compiler unlock locks or free
   memory when the function exits.

 - Update the Rust tracepoint code to use the C code too

   There was some duplication of the tracepoint code for Rust that did
   the same logic as the C code. Add a helper that makes it possible for
   both algorithms to use the same logic in one place.

 - Add poll to trace event hist files

   It is useful to know when an event is triggered, or even with some
   filtering. Since hist files of events get updated when active and the
   event is triggered, allow applications to poll the hist file and wake
   up when an event is triggered. This will let the application know
   that the event it is waiting for happened.

 - Add :mod: command to enable events for current or future modules

   The function tracer already has a way to enable functions to be
   traced in modules by writing ":mod:<module>" into set_ftrace_filter.
   That will enable either all the functions for the module if it is
   loaded, or if it is not, it will cache that command, and when the
   module is loaded that matches <module>, its functions will be
   enabled. This also allows init functions to be traced. But currently
   events do not have that feature.

   Add the command where if ':mod:<module>' is written into set_event,
   then either all the modules events are enabled if it is loaded, or
   cache it so that the module's events are enabled when it is loaded.
   This also works from the kernel command line, where
   "trace_event=:mod:<module>", when the module is loaded at boot up,
   its events will be enabled then.

* tag 'trace-v6.14-3' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (26 commits)
  tracing: Fix output of set_event for some cached module events
  tracing: Fix allocation of printing set_event file content
  tracing: Rename update_cache() to update_mod_cache()
  tracing: Fix #if CONFIG_MODULES to #ifdef CONFIG_MODULES
  selftests/ftrace: Add test that tests event :mod: commands
  tracing: Cache ":mod:" events for modules not loaded yet
  tracing: Add :mod: command to enabled module events
  selftests/tracing: Add hist poll() support test
  tracing/hist: Support POLLPRI event for poll on histogram
  tracing/hist: Add poll(POLLIN) support on hist file
  tracing: Fix using ret variable in tracing_set_tracer()
  tracepoint: Reduce duplication of __DO_TRACE_CALL
  tracing/string: Create and use __free(argv_free) in trace_dynevent.c
  tracing: Switch trace_stat.c code over to use guard()
  tracing: Switch trace_stack.c code over to use guard()
  tracing: Switch trace_osnoise.c code over to use guard() and __free()
  tracing: Switch trace_events_synth.c code over to use guard()
  tracing: Switch trace_events_filter.c code over to use guard()
  tracing: Switch trace_events_trigger.c code over to use guard()
  tracing: Switch trace_events_hist.c code over to use guard()
  ...
This commit is contained in:
Linus Torvalds
2025-01-23 17:51:16 -08:00
21 changed files with 1054 additions and 482 deletions

View File

@@ -7161,6 +7161,14 @@
comma-separated list of trace events to enable. See
also Documentation/trace/events.rst
To enable modules, use :mod: keyword:
trace_event=:mod:<module>
The value before :mod: will only enable specific events
that are part of the module. See the above mentioned
document for more information.
trace_instance=[instance-info]
[FTRACE] Create a ring buffer instance early in boot up.
This will be listed in:

View File

@@ -55,6 +55,30 @@ command::
# echo 'irq:*' > /sys/kernel/tracing/set_event
The set_event file may also be used to enable events associated to only
a specific module::
# echo ':mod:<module>' > /sys/kernel/tracing/set_event
Will enable all events in the module ``<module>``. If the module is not yet
loaded, the string will be saved and when a module is that matches ``<module>``
is loaded, then it will apply the enabling of events then.
The text before ``:mod:`` will be parsed to specify specific events that the
module creates::
# echo '<match>:mod:<module>' > /sys/kernel/tracing/set_event
The above will enable any system or event that ``<match>`` matches. If
``<match>`` is ``"*"`` then it will match all events.
To enable only a specific event within a system::
# echo '<system>:<event>:mod:<module>' > /sys/kernel/tracing/set_event
If ``<event>`` is ``"*"`` then it will match all events within the system
for a given module.
2.2 Via the 'enable' toggle
---------------------------

View File

@@ -4,6 +4,7 @@
#include <linux/args.h>
#include <linux/array_size.h>
#include <linux/cleanup.h> /* for DEFINE_FREE() */
#include <linux/compiler.h> /* for inline */
#include <linux/types.h> /* for size_t */
#include <linux/stddef.h> /* for NULL */
@@ -312,6 +313,8 @@ extern void *kmemdup_array(const void *src, size_t count, size_t element_size, g
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
extern void argv_free(char **argv);
DEFINE_FREE(argv_free, char **, if (!IS_ERR_OR_NULL(_T)) argv_free(_T))
/* lib/cmdline.c */
extern int get_option(char **str, int *pint);
extern char *get_options(const char *str, int nints, int *ints);

View File

@@ -673,6 +673,20 @@ struct trace_event_file {
atomic_t tm_ref; /* trigger-mode reference counter */
};
#ifdef CONFIG_HIST_TRIGGERS
extern struct irq_work hist_poll_work;
extern wait_queue_head_t hist_poll_wq;
static inline void hist_poll_wakeup(void)
{
if (wq_has_sleeper(&hist_poll_wq))
irq_work_queue(&hist_poll_work);
}
#define hist_poll_wait(file, wait) \
poll_wait(file, &hist_poll_wq, wait)
#endif
#define __TRACE_EVENT_FLAGS(name, value) \
static int __init trace_init_flags_##name(void) \
{ \

View File

@@ -218,7 +218,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
#define __DEFINE_RUST_DO_TRACE(name, proto, args) \
notrace void rust_do_trace_##name(proto) \
{ \
__rust_do_trace_##name(args); \
__do_trace_##name(args); \
}
/*
@@ -268,7 +268,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \
__DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \
static inline void __rust_do_trace_##name(proto) \
static inline void __do_trace_##name(proto) \
{ \
if (cond) { \
guard(preempt_notrace)(); \
@@ -277,12 +277,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
} \
static inline void trace_##name(proto) \
{ \
if (static_branch_unlikely(&__tracepoint_##name.key)) { \
if (cond) { \
guard(preempt_notrace)(); \
__DO_TRACE_CALL(name, TP_ARGS(args)); \
} \
} \
if (static_branch_unlikely(&__tracepoint_##name.key)) \
__do_trace_##name(args); \
if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { \
WARN_ONCE(!rcu_is_watching(), \
"RCU not watching for tracepoint"); \
@@ -291,7 +287,7 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
#define __DECLARE_TRACE_SYSCALL(name, proto, args, data_proto) \
__DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \
static inline void __rust_do_trace_##name(proto) \
static inline void __do_trace_##name(proto) \
{ \
guard(rcu_tasks_trace)(); \
__DO_TRACE_CALL(name, TP_ARGS(args)); \
@@ -299,10 +295,8 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
static inline void trace_##name(proto) \
{ \
might_fault(); \
if (static_branch_unlikely(&__tracepoint_##name.key)) { \
guard(rcu_tasks_trace)(); \
__DO_TRACE_CALL(name, TP_ARGS(args)); \
} \
if (static_branch_unlikely(&__tracepoint_##name.key)) \
__do_trace_##name(args); \
if (IS_ENABLED(CONFIG_LOCKDEP)) { \
WARN_ONCE(!rcu_is_watching(), \
"RCU not watching for tracepoint"); \

View File

@@ -4908,23 +4908,6 @@ static int ftrace_hash_move_and_update_ops(struct ftrace_ops *ops,
return __ftrace_hash_move_and_update_ops(ops, orig_hash, hash, enable);
}
static bool module_exists(const char *module)
{
/* All modules have the symbol __this_module */
static const char this_mod[] = "__this_module";
char modname[MAX_PARAM_PREFIX_LEN + sizeof(this_mod) + 2];
unsigned long val;
int n;
n = snprintf(modname, sizeof(modname), "%s:%s", module, this_mod);
if (n > sizeof(modname) - 1)
return false;
val = module_kallsyms_lookup_name(modname);
return val != 0;
}
static int cache_mod(struct trace_array *tr,
const char *func, char *module, int enable)
{

File diff suppressed because it is too large Load Diff

View File

@@ -400,6 +400,9 @@ struct trace_array {
cpumask_var_t pipe_cpumask;
int ref;
int trace_ref;
#ifdef CONFIG_MODULES
struct list_head mod_events;
#endif
#ifdef CONFIG_FUNCTION_TRACER
struct ftrace_ops *ops;
struct trace_pid_list __rcu *function_pids;
@@ -435,6 +438,15 @@ enum {
TRACE_ARRAY_FL_MOD_INIT = BIT(2),
};
#ifdef CONFIG_MODULES
bool module_exists(const char *module);
#else
static inline bool module_exists(const char *module)
{
return false;
}
#endif
extern struct list_head ftrace_trace_arrays;
extern struct mutex trace_types_lock;

View File

@@ -74,24 +74,19 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *type
struct dyn_event *pos, *n;
char *system = NULL, *event, *p;
int argc, ret = -ENOENT;
char **argv;
char **argv __free(argv_free) = argv_split(GFP_KERNEL, raw_command, &argc);
argv = argv_split(GFP_KERNEL, raw_command, &argc);
if (!argv)
return -ENOMEM;
if (argv[0][0] == '-') {
if (argv[0][1] != ':') {
ret = -EINVAL;
goto out;
}
if (argv[0][1] != ':')
return -EINVAL;
event = &argv[0][2];
} else {
event = strchr(argv[0], ':');
if (!event) {
ret = -EINVAL;
goto out;
}
if (!event)
return -EINVAL;
event++;
}
@@ -101,10 +96,8 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *type
event = p + 1;
*p = '\0';
}
if (!system && event[0] == '\0') {
ret = -EINVAL;
goto out;
}
if (!system && event[0] == '\0')
return -EINVAL;
mutex_lock(&event_mutex);
for_each_dyn_event_safe(pos, n) {
@@ -120,8 +113,6 @@ int dyn_event_release(const char *raw_command, struct dyn_event_operations *type
}
tracing_reset_all_online_cpus();
mutex_unlock(&event_mutex);
out:
argv_free(argv);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -2405,13 +2405,11 @@ int apply_subsystem_event_filter(struct trace_subsystem_dir *dir,
struct event_filter *filter = NULL;
int err = 0;
mutex_lock(&event_mutex);
guard(mutex)(&event_mutex);
/* Make sure the system still has events */
if (!dir->nr_events) {
err = -ENODEV;
goto out_unlock;
}
if (!dir->nr_events)
return -ENODEV;
if (!strcmp(strstrip(filter_string), "0")) {
filter_free_subsystem_preds(dir, tr);
@@ -2422,7 +2420,7 @@ int apply_subsystem_event_filter(struct trace_subsystem_dir *dir,
tracepoint_synchronize_unregister();
filter_free_subsystem_filters(dir, tr);
__free_filter(filter);
goto out_unlock;
return 0;
}
err = create_system_filter(dir, filter_string, &filter);
@@ -2434,8 +2432,6 @@ int apply_subsystem_event_filter(struct trace_subsystem_dir *dir,
__free_filter(system->filter);
system->filter = filter;
}
out_unlock:
mutex_unlock(&event_mutex);
return err;
}
@@ -2612,17 +2608,15 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id,
struct event_filter *filter = NULL;
struct trace_event_call *call;
mutex_lock(&event_mutex);
guard(mutex)(&event_mutex);
call = event->tp_event;
err = -EINVAL;
if (!call)
goto out_unlock;
return -EINVAL;
err = -EEXIST;
if (event->filter)
goto out_unlock;
return -EEXIST;
err = create_filter(NULL, call, filter_str, false, &filter);
if (err)
@@ -2637,9 +2631,6 @@ free_filter:
if (err || ftrace_event_is_function(call))
__free_filter(filter);
out_unlock:
mutex_unlock(&event_mutex);
return err;
}

View File

@@ -5311,6 +5311,8 @@ static void event_hist_trigger(struct event_trigger_data *data,
if (resolve_var_refs(hist_data, key, var_ref_vals, true))
hist_trigger_actions(hist_data, elt, buffer, rec, rbe, key, var_ref_vals);
hist_poll_wakeup();
}
static void hist_trigger_stacktrace_print(struct seq_file *m,
@@ -5590,49 +5592,128 @@ static void hist_trigger_show(struct seq_file *m,
n_entries, (u64)atomic64_read(&hist_data->map->drops));
}
struct hist_file_data {
struct file *file;
u64 last_read;
u64 last_act;
};
static u64 get_hist_hit_count(struct trace_event_file *event_file)
{
struct hist_trigger_data *hist_data;
struct event_trigger_data *data;
u64 ret = 0;
list_for_each_entry(data, &event_file->triggers, list) {
if (data->cmd_ops->trigger_type == ETT_EVENT_HIST) {
hist_data = data->private_data;
ret += atomic64_read(&hist_data->map->hits);
}
}
return ret;
}
static int hist_show(struct seq_file *m, void *v)
{
struct hist_file_data *hist_file = m->private;
struct event_trigger_data *data;
struct trace_event_file *event_file;
int n = 0, ret = 0;
int n = 0;
mutex_lock(&event_mutex);
guard(mutex)(&event_mutex);
event_file = event_file_file(m->private);
if (unlikely(!event_file)) {
ret = -ENODEV;
goto out_unlock;
}
event_file = event_file_file(hist_file->file);
if (unlikely(!event_file))
return -ENODEV;
list_for_each_entry(data, &event_file->triggers, list) {
if (data->cmd_ops->trigger_type == ETT_EVENT_HIST)
hist_trigger_show(m, data, n++);
}
hist_file->last_read = get_hist_hit_count(event_file);
/*
* Update last_act too so that poll()/POLLPRI can wait for the next
* event after any syscall on hist file.
*/
hist_file->last_act = hist_file->last_read;
out_unlock:
mutex_unlock(&event_mutex);
return 0;
}
static __poll_t event_hist_poll(struct file *file, struct poll_table_struct *wait)
{
struct trace_event_file *event_file;
struct seq_file *m = file->private_data;
struct hist_file_data *hist_file = m->private;
__poll_t ret = 0;
u64 cnt;
guard(mutex)(&event_mutex);
event_file = event_file_data(file);
if (!event_file)
return EPOLLERR;
hist_poll_wait(file, wait);
cnt = get_hist_hit_count(event_file);
if (hist_file->last_read != cnt)
ret |= EPOLLIN | EPOLLRDNORM;
if (hist_file->last_act != cnt) {
hist_file->last_act = cnt;
ret |= EPOLLPRI;
}
return ret;
}
static int event_hist_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
struct hist_file_data *hist_file = m->private;
kfree(hist_file);
return tracing_single_release_file_tr(inode, file);
}
static int event_hist_open(struct inode *inode, struct file *file)
{
struct trace_event_file *event_file;
struct hist_file_data *hist_file;
int ret;
ret = tracing_open_file_tr(inode, file);
if (ret)
return ret;
guard(mutex)(&event_mutex);
event_file = event_file_data(file);
if (!event_file)
return -ENODEV;
hist_file = kzalloc(sizeof(*hist_file), GFP_KERNEL);
if (!hist_file)
return -ENOMEM;
hist_file->file = file;
hist_file->last_act = get_hist_hit_count(event_file);
/* Clear private_data to avoid warning in single_open() */
file->private_data = NULL;
return single_open(file, hist_show, file);
ret = single_open(file, hist_show, hist_file);
if (ret)
kfree(hist_file);
return ret;
}
const struct file_operations event_hist_fops = {
.open = event_hist_open,
.read = seq_read,
.llseek = seq_lseek,
.release = tracing_single_release_file_tr,
.release = event_hist_release,
.poll = event_hist_poll,
};
#ifdef CONFIG_HIST_TRIGGERS_DEBUG
@@ -5873,25 +5954,19 @@ static int hist_debug_show(struct seq_file *m, void *v)
{
struct event_trigger_data *data;
struct trace_event_file *event_file;
int n = 0, ret = 0;
int n = 0;
mutex_lock(&event_mutex);
guard(mutex)(&event_mutex);
event_file = event_file_file(m->private);
if (unlikely(!event_file)) {
ret = -ENODEV;
goto out_unlock;
}
if (unlikely(!event_file))
return -ENODEV;
list_for_each_entry(data, &event_file->triggers, list) {
if (data->cmd_ops->trigger_type == ETT_EVENT_HIST)
hist_trigger_debug_show(m, data, n++);
}
out_unlock:
mutex_unlock(&event_mutex);
return ret;
return 0;
}
static int event_hist_debug_open(struct inode *inode, struct file *file)

View File

@@ -49,16 +49,11 @@ static char *last_cmd;
static int errpos(const char *str)
{
int ret = 0;
mutex_lock(&lastcmd_mutex);
guard(mutex)(&lastcmd_mutex);
if (!str || !last_cmd)
goto out;
return 0;
ret = err_pos(last_cmd, str);
out:
mutex_unlock(&lastcmd_mutex);
return ret;
return err_pos(last_cmd, str);
}
static void last_cmd_set(const char *str)
@@ -74,14 +69,12 @@ static void last_cmd_set(const char *str)
static void synth_err(u8 err_type, u16 err_pos)
{
mutex_lock(&lastcmd_mutex);
guard(mutex)(&lastcmd_mutex);
if (!last_cmd)
goto out;
return;
tracing_log_err(NULL, "synthetic_events", last_cmd, err_text,
err_type, err_pos);
out:
mutex_unlock(&lastcmd_mutex);
}
static int create_synth_event(const char *raw_command);

View File

@@ -211,12 +211,10 @@ static int event_trigger_regex_open(struct inode *inode, struct file *file)
if (ret)
return ret;
mutex_lock(&event_mutex);
guard(mutex)(&event_mutex);
if (unlikely(!event_file_file(file))) {
mutex_unlock(&event_mutex);
if (unlikely(!event_file_file(file)))
return -ENODEV;
}
if ((file->f_mode & FMODE_WRITE) &&
(file->f_flags & O_TRUNC)) {
@@ -239,8 +237,6 @@ static int event_trigger_regex_open(struct inode *inode, struct file *file)
}
}
mutex_unlock(&event_mutex);
return ret;
}
@@ -248,7 +244,6 @@ int trigger_process_regex(struct trace_event_file *file, char *buff)
{
char *command, *next;
struct event_command *p;
int ret = -EINVAL;
next = buff = skip_spaces(buff);
command = strsep(&next, ": \t");
@@ -259,17 +254,14 @@ int trigger_process_regex(struct trace_event_file *file, char *buff)
}
command = (command[0] != '!') ? command : command + 1;
mutex_lock(&trigger_cmd_mutex);
list_for_each_entry(p, &trigger_commands, list) {
if (strcmp(p->name, command) == 0) {
ret = p->parse(p, file, buff, command, next);
goto out_unlock;
}
}
out_unlock:
mutex_unlock(&trigger_cmd_mutex);
guard(mutex)(&trigger_cmd_mutex);
return ret;
list_for_each_entry(p, &trigger_commands, list) {
if (strcmp(p->name, command) == 0)
return p->parse(p, file, buff, command, next);
}
return -EINVAL;
}
static ssize_t event_trigger_regex_write(struct file *file,
@@ -278,7 +270,7 @@ static ssize_t event_trigger_regex_write(struct file *file,
{
struct trace_event_file *event_file;
ssize_t ret;
char *buf;
char *buf __free(kfree) = NULL;
if (!cnt)
return 0;
@@ -292,24 +284,18 @@ static ssize_t event_trigger_regex_write(struct file *file,
strim(buf);
mutex_lock(&event_mutex);
event_file = event_file_file(file);
if (unlikely(!event_file)) {
mutex_unlock(&event_mutex);
kfree(buf);
return -ENODEV;
}
ret = trigger_process_regex(event_file, buf);
mutex_unlock(&event_mutex);
guard(mutex)(&event_mutex);
kfree(buf);
event_file = event_file_file(file);
if (unlikely(!event_file))
return -ENODEV;
ret = trigger_process_regex(event_file, buf);
if (ret < 0)
goto out;
return ret;
*ppos += cnt;
ret = cnt;
out:
return ret;
return cnt;
}
static int event_trigger_regex_release(struct inode *inode, struct file *file)
@@ -359,20 +345,16 @@ const struct file_operations event_trigger_fops = {
__init int register_event_command(struct event_command *cmd)
{
struct event_command *p;
int ret = 0;
mutex_lock(&trigger_cmd_mutex);
guard(mutex)(&trigger_cmd_mutex);
list_for_each_entry(p, &trigger_commands, list) {
if (strcmp(cmd->name, p->name) == 0) {
ret = -EBUSY;
goto out_unlock;
}
if (strcmp(cmd->name, p->name) == 0)
return -EBUSY;
}
list_add(&cmd->list, &trigger_commands);
out_unlock:
mutex_unlock(&trigger_cmd_mutex);
return ret;
return 0;
}
/*
@@ -382,20 +364,17 @@ __init int register_event_command(struct event_command *cmd)
__init int unregister_event_command(struct event_command *cmd)
{
struct event_command *p, *n;
int ret = -ENODEV;
mutex_lock(&trigger_cmd_mutex);
guard(mutex)(&trigger_cmd_mutex);
list_for_each_entry_safe(p, n, &trigger_commands, list) {
if (strcmp(cmd->name, p->name) == 0) {
ret = 0;
list_del_init(&p->list);
goto out_unlock;
return 0;
}
}
out_unlock:
mutex_unlock(&trigger_cmd_mutex);
return ret;
return -ENODEV;
}
/**

View File

@@ -2083,26 +2083,21 @@ static void osnoise_hotplug_workfn(struct work_struct *dummy)
{
unsigned int cpu = smp_processor_id();
mutex_lock(&trace_types_lock);
guard(mutex)(&trace_types_lock);
if (!osnoise_has_registered_instances())
goto out_unlock_trace;
return;
mutex_lock(&interface_lock);
cpus_read_lock();
guard(mutex)(&interface_lock);
guard(cpus_read_lock)();
if (!cpu_online(cpu))
goto out_unlock;
return;
if (!cpumask_test_cpu(cpu, &osnoise_cpumask))
goto out_unlock;
return;
start_kthread(cpu);
out_unlock:
cpus_read_unlock();
mutex_unlock(&interface_lock);
out_unlock_trace:
mutex_unlock(&trace_types_lock);
}
static DECLARE_WORK(osnoise_hotplug_work, osnoise_hotplug_workfn);
@@ -2300,31 +2295,22 @@ static ssize_t
osnoise_cpus_read(struct file *filp, char __user *ubuf, size_t count,
loff_t *ppos)
{
char *mask_str;
char *mask_str __free(kfree) = NULL;
int len;
mutex_lock(&interface_lock);
guard(mutex)(&interface_lock);
len = snprintf(NULL, 0, "%*pbl\n", cpumask_pr_args(&osnoise_cpumask)) + 1;
mask_str = kmalloc(len, GFP_KERNEL);
if (!mask_str) {
count = -ENOMEM;
goto out_unlock;
}
if (!mask_str)
return -ENOMEM;
len = snprintf(mask_str, len, "%*pbl\n", cpumask_pr_args(&osnoise_cpumask));
if (len >= count) {
count = -EINVAL;
goto out_free;
}
if (len >= count)
return -EINVAL;
count = simple_read_from_buffer(ubuf, count, ppos, mask_str, len);
out_free:
kfree(mask_str);
out_unlock:
mutex_unlock(&interface_lock);
return count;
}

View File

@@ -520,20 +520,18 @@ stack_trace_sysctl(const struct ctl_table *table, int write, void *buffer,
int was_enabled;
int ret;
mutex_lock(&stack_sysctl_mutex);
guard(mutex)(&stack_sysctl_mutex);
was_enabled = !!stack_tracer_enabled;
ret = proc_dointvec(table, write, buffer, lenp, ppos);
if (ret || !write || (was_enabled == !!stack_tracer_enabled))
goto out;
return ret;
if (stack_tracer_enabled)
register_ftrace_function(&trace_ops);
else
unregister_ftrace_function(&trace_ops);
out:
mutex_unlock(&stack_sysctl_mutex);
return ret;
}

View File

@@ -128,7 +128,7 @@ static int stat_seq_init(struct stat_session *session)
int ret = 0;
int i;
mutex_lock(&session->stat_mutex);
guard(mutex)(&session->stat_mutex);
__reset_stat_session(session);
if (!ts->stat_cmp)
@@ -136,11 +136,11 @@ static int stat_seq_init(struct stat_session *session)
stat = ts->stat_start(ts);
if (!stat)
goto exit;
return 0;
ret = insert_stat(root, stat, ts->stat_cmp);
if (ret)
goto exit;
return ret;
/*
* Iterate over the tracer stat entries and store them in an rbtree.
@@ -157,13 +157,10 @@ static int stat_seq_init(struct stat_session *session)
goto exit_free_rbtree;
}
exit:
mutex_unlock(&session->stat_mutex);
return ret;
exit_free_rbtree:
__reset_stat_session(session);
mutex_unlock(&session->stat_mutex);
return ret;
}
@@ -308,7 +305,7 @@ static int init_stat_file(struct stat_session *session)
int register_stat_tracer(struct tracer_stat *trace)
{
struct stat_session *session, *node;
int ret = -EINVAL;
int ret;
if (!trace)
return -EINVAL;
@@ -316,18 +313,18 @@ int register_stat_tracer(struct tracer_stat *trace)
if (!trace->stat_start || !trace->stat_next || !trace->stat_show)
return -EINVAL;
guard(mutex)(&all_stat_sessions_mutex);
/* Already registered? */
mutex_lock(&all_stat_sessions_mutex);
list_for_each_entry(node, &all_stat_sessions, session_list) {
if (node->ts == trace)
goto out;
return -EINVAL;
}
ret = -ENOMEM;
/* Init the session */
session = kzalloc(sizeof(*session), GFP_KERNEL);
if (!session)
goto out;
return -ENOMEM;
session->ts = trace;
INIT_LIST_HEAD(&session->session_list);
@@ -336,16 +333,13 @@ int register_stat_tracer(struct tracer_stat *trace)
ret = init_stat_file(session);
if (ret) {
destroy_session(session);
goto out;
return ret;
}
ret = 0;
/* Register */
list_add_tail(&session->session_list, &all_stat_sessions);
out:
mutex_unlock(&all_stat_sessions_mutex);
return ret;
return 0;
}
void unregister_stat_tracer(struct tracer_stat *trace)

View File

@@ -6,4 +6,6 @@ TEST_PROGS := ftracetest-ktap
TEST_FILES := test.d settings
EXTRA_CLEAN := $(OUTPUT)/logs/*
TEST_GEN_PROGS = poll
include ../lib.mk

View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Simple poll on a file.
*
* Copyright (c) 2024 Google LLC.
*/
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE 4096
/*
* Usage:
* poll [-I|-P] [-t timeout] FILE
*/
int main(int argc, char *argv[])
{
struct pollfd pfd = {.events = POLLIN};
char buf[BUFSIZE];
int timeout = -1;
int ret, opt;
while ((opt = getopt(argc, argv, "IPt:")) != -1) {
switch (opt) {
case 'I':
pfd.events = POLLIN;
break;
case 'P':
pfd.events = POLLPRI;
break;
case 't':
timeout = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: %s [-I|-P] [-t timeout] FILE\n",
argv[0]);
return -1;
}
}
if (optind >= argc) {
fprintf(stderr, "Error: Polling file is not specified\n");
return -1;
}
pfd.fd = open(argv[optind], O_RDONLY);
if (pfd.fd < 0) {
fprintf(stderr, "failed to open %s", argv[optind]);
perror("open");
return -1;
}
/* Reset poll by read if POLLIN is specified. */
if (pfd.events & POLLIN)
do {} while (read(pfd.fd, buf, BUFSIZE) == BUFSIZE);
ret = poll(&pfd, 1, timeout);
if (ret < 0 && errno != EINTR) {
perror("poll");
return -1;
}
close(pfd.fd);
/* If timeout happned (ret == 0), exit code is 1 */
if (ret == 0)
return 1;
return 0;
}

View File

@@ -0,0 +1,191 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: event tracing - enable/disable with module event
# requires: set_event "Can enable module events via: :mod:":README
# flags: instance
rmmod trace-events-sample ||:
if ! modprobe trace-events-sample ; then
echo "No trace-events sample module - please make CONFIG_SAMPLE_TRACE_EVENTS=m"
exit_unresolved;
fi
trap "rmmod trace-events-sample" EXIT
# Set events for the module
echo ":mod:trace-events-sample" > set_event
test_all_enabled() {
# Check if more than one is enabled
grep -q sample-trace:foo_bar set_event
grep -q sample-trace:foo_bar_with_cond set_event
grep -q sample-trace:foo_bar_with_fn set_event
# All of them should be enabled. Check via the enable file
val=`cat events/sample-trace/enable`
if [ $val -ne 1 ]; then
exit_fail
fi
}
clear_events() {
echo > set_event
val=`cat events/enable`
if [ "$val" != "0" ]; then
exit_fail
fi
count=`cat set_event | wc -l`
if [ $count -ne 0 ]; then
exit_fail
fi
}
test_all_enabled
echo clear all events
echo 0 > events/enable
echo Confirm the events are disabled
val=`cat events/sample-trace/enable`
if [ $val -ne 0 ]; then
exit_fail
fi
echo And the set_event file is empty
cnt=`wc -l set_event`
if [ $cnt -ne 0 ]; then
exit_fail
fi
echo now enable all events
echo 1 > events/enable
echo Confirm the events are enabled again
val=`cat events/sample-trace/enable`
if [ $val -ne 1 ]; then
exit_fail
fi
echo disable just the module events
echo '!:mod:trace-events-sample' >> set_event
echo Should have mix of events enabled
val=`cat events/enable`
if [ "$val" != "X" ]; then
exit_fail
fi
echo Confirm the module events are disabled
val=`cat events/sample-trace/enable`
if [ $val -ne 0 ]; then
exit_fail
fi
echo 0 > events/enable
echo now enable the system events
echo 'sample-trace:mod:trace-events-sample' > set_event
test_all_enabled
echo clear all events
echo 0 > events/enable
echo Confirm the events are disabled
val=`cat events/sample-trace/enable`
if [ $val -ne 0 ]; then
exit_fail
fi
echo Test enabling foo_bar only
echo 'foo_bar:mod:trace-events-sample' > set_event
grep -q sample-trace:foo_bar set_event
echo make sure nothing is found besides foo_bar
if grep -q -v sample-trace:foo_bar set_event ; then
exit_fail
fi
echo Append another using the system and event name
echo 'sample-trace:foo_bar_with_cond:mod:trace-events-sample' >> set_event
grep -q sample-trace:foo_bar set_event
grep -q sample-trace:foo_bar_with_cond set_event
count=`cat set_event | wc -l`
if [ $count -ne 2 ]; then
exit_fail
fi
clear_events
rmmod trace-events-sample
echo ':mod:trace-events-sample' > set_event
echo make sure that the module shows up, and '-' is converted to '_'
grep -q '\*:\*:mod:trace_events_sample' set_event
modprobe trace-events-sample
test_all_enabled
clear_events
rmmod trace-events-sample
echo Enable just the system events
echo 'sample-trace:mod:trace-events-sample' > set_event
grep -q 'sample-trace:mod:trace_events_sample' set_event
modprobe trace-events-sample
test_all_enabled
clear_events
rmmod trace-events-sample
echo Enable event with just event name
echo 'foo_bar:mod:trace-events-sample' > set_event
grep -q 'foo_bar:mod:trace_events_sample' set_event
echo Enable another event with both system and event name
echo 'sample-trace:foo_bar_with_cond:mod:trace-events-sample' >> set_event
grep -q 'sample-trace:foo_bar_with_cond:mod:trace_events_sample' set_event
echo Make sure the other event was still there
grep -q 'foo_bar:mod:trace_events_sample' set_event
modprobe trace-events-sample
echo There should be no :mod: cached events
if grep -q ':mod:' set_event; then
exit_fail
fi
echo two events should be enabled
count=`cat set_event | wc -l`
if [ $count -ne 2 ]; then
exit_fail
fi
echo only two events should be enabled
val=`cat events/sample-trace/enable`
if [ "$val" != "X" ]; then
exit_fail
fi
val=`cat events/sample-trace/foo_bar/enable`
if [ "$val" != "1" ]; then
exit_fail
fi
val=`cat events/sample-trace/foo_bar_with_cond/enable`
if [ "$val" != "1" ]; then
exit_fail
fi
clear_trace

Some files were not shown because too many files have changed in this diff Show More