// SPDX-License-Identifier: GPL-2.0 /* * event tracer * * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> * * - Added format output of fields of the trace point. * This was based off of work by Tom Zanussi <tzanussi@gmail.com>. *
*/
/* Double loops, do not use break, only goto's work */ #define do_for_each_event_file(tr, file) \
list_for_each_entry(tr, &ftrace_trace_arrays, list) { \
list_for_each_entry(file, &tr->events, list)
staticint __trace_define_field(struct list_head *head, constchar *type, constchar *name, int offset, int size, int is_signed, int filter_type, int len, int need_test)
{ struct ftrace_event_field *field;
field = kmem_cache_alloc(field_cachep, GFP_TRACE); if (!field) return -ENOMEM;
int trace_define_field(struct trace_event_call *call, constchar *type, constchar *name, int offset, int size, int is_signed, int filter_type)
{ struct list_head *head;
staticint trace_define_field_ext(struct trace_event_call *call, constchar *type, constchar *name, int offset, int size, int is_signed, int filter_type, int len, int need_test)
{ struct list_head *head;
/* * run-time version of trace_event_get_offsets_<call>() that returns the last * accessible offset of trace fields excluding __dynamic_array bytes
*/ int trace_event_get_offsets(struct trace_event_call *call)
{ struct ftrace_event_field *tail; struct list_head *head;
head = trace_get_fields(call); /* * head->next points to the last field with the largest offset, * since it was added last by trace_define_field()
*/
tail = list_first_entry(head, struct ftrace_event_field, link); return tail->offset + tail->size;
}
if (!(len = str_has_prefix(fmt, "REC->"))) return NULL;
fmt += len; for (p = fmt; *p; p++) { if (!isalnum(*p) && *p != '_') break;
}
len = p - fmt;
for (; field->type; field++) { if (strncmp(field->name, fmt, len) || field->name[len]) continue;
return field;
} return NULL;
}
/* * Check if the referenced field is an array and return true, * as arrays are OK to dereference.
*/ staticbool test_field(constchar *fmt, struct trace_event_call *call)
{ struct trace_event_fields *field;
field = find_event_field(fmt, call); if (!field) returnfalse;
/* This is an array and is OK to dereference. */ return strchr(field->type, '[') != NULL;
}
/* Look for a string within an argument */ staticbool find_print_string(constchar *arg, constchar *str, constchar *end)
{ constchar *r;
r = strstr(arg, str); return r && r < end;
}
/* Return true if the argument pointer is safe */ staticbool process_pointer(constchar *fmt, int len, struct trace_event_call *call)
{ constchar *r, *e, *a;
e = fmt + len;
/* Find the REC-> in the argument */
r = strstr(fmt, "REC->"); if (r && r < e) { /* * Addresses of events on the buffer, or an array on the buffer is * OK to dereference. There's ways to fool this, but * this is to catch common mistakes, not malicious code.
*/
a = strchr(fmt, '&'); if ((a && (a < r)) || test_field(r, call)) returntrue;
} elseif (find_print_string(fmt, "__get_dynamic_array(", e)) { returntrue;
} elseif (find_print_string(fmt, "__get_rel_dynamic_array(", e)) { returntrue;
} elseif (find_print_string(fmt, "__get_dynamic_array_len(", e)) { returntrue;
} elseif (find_print_string(fmt, "__get_rel_dynamic_array_len(", e)) { returntrue;
} elseif (find_print_string(fmt, "__get_sockaddr(", e)) { returntrue;
} elseif (find_print_string(fmt, "__get_rel_sockaddr(", e)) { returntrue;
} returnfalse;
}
/* Return true if the string is safe */ staticbool process_string(constchar *fmt, int len, struct trace_event_call *call)
{ struct trace_event_fields *field; constchar *r, *e, *s;
e = fmt + len;
/* * There are several helper functions that return strings. * If the argument contains a function, then assume its field is valid. * It is considered that the argument has a function if it has: * alphanumeric or '_' before a parenthesis.
*/
s = fmt; do {
r = strstr(s, "("); if (!r || r >= e) break; for (int i = 1; r - i >= s; i++) { char ch = *(r - i); if (isspace(ch)) continue; if (isalnum(ch) || ch == '_') returntrue; /* Anything else, this isn't a function */ break;
} /* A function could be wrapped in parethesis, try the next one */
s = r + 1;
} while (s < e);
/* * Check for arrays. If the argument has: foo[REC->val] * then it is very likely that foo is an array of strings * that are safe to use.
*/
r = strstr(s, "["); if (r && r < e) {
r = strstr(r, "REC->"); if (r && r < e) returntrue;
}
/* * If there's any strings in the argument consider this arg OK as it * could be: REC->field ? "foo" : "bar" and we don't want to get into * verifying that logic here.
*/ if (find_print_string(fmt, "\"", e)) returntrue;
/* Dereferenced strings are also valid like any other pointer */ if (process_pointer(fmt, len, call)) returntrue;
/* Make sure the field is found */
field = find_event_field(fmt, call); if (!field) returnfalse;
/* Test this field's string before printing the event */
call->flags |= TRACE_EVENT_FL_TEST_STR;
field->needs_test = 1;
/* * Examine the print fmt of the event looking for unsafe dereference * pointers using %p* that could be recorded in the trace event and * much later referenced after the pointer was freed. Dereferencing * pointers are OK, if it is dereferenced into the event itself.
*/ staticvoid test_event_printk(struct trace_event_call *call)
{
u64 dereference_flags = 0;
u64 string_flags = 0; bool first = true; constchar *fmt; int parens = 0; char in_quote = 0; int start_arg = 0; int arg = 0; int i, e;
fmt = call->print_fmt;
if (!fmt) return;
for (i = 0; fmt[i]; i++) { switch (fmt[i]) { case'\\':
i++; if (!fmt[i]) return; continue; case'"': case'\'': /* * The print fmt starts with a string that * is processed first to find %p* usage, * then after the first string, the print fmt * contains arguments that are used to check * if the dereferenced %p* usage is safe.
*/ if (first) { if (fmt[i] == '\'') continue; if (in_quote) {
arg = 0;
first = false; /* * If there was no %p* uses * the fmt is OK.
*/ if (!dereference_flags) return;
}
} if (in_quote) { if (in_quote == fmt[i])
in_quote = 0;
} else {
in_quote = fmt[i];
} continue; case'%': if (!first || !in_quote) continue;
i++; if (!fmt[i]) return; switch (fmt[i]) { case'%': continue; case'p':
do_pointer: /* Find dereferencing fields */ switch (fmt[i + 1]) { case'B': case'R': case'r': case'b': case'M': case'm': case'I': case'i': case'E': case'U': case'V': case'N': case'a': case'd': case'D': case'g': case't': case'C': case'O': case'f': if (WARN_ONCE(arg == 63, "Too many args for event: %s",
trace_event_name(call))) return;
dereference_flags |= 1ULL << arg;
} break; default:
{ bool star = false; int j;
/* Increment arg if %*s exists. */ for (j = 0; fmt[i + j]; j++) { if (isdigit(fmt[i + j]) ||
fmt[i + j] == '.') continue; if (fmt[i + j] == '*') {
star = true; /* Handle %*pbl case */ if (!j && fmt[i + 1] == 'p') {
arg++;
i++; goto do_pointer;
} continue;
} if ((fmt[i + j] == 's')) { if (star)
arg++; if (WARN_ONCE(arg == 63, "Too many args for event: %s",
trace_event_name(call))) return;
dereference_flags |= 1ULL << arg;
string_flags |= 1ULL << arg;
} break;
} break;
} /* default */
} /* switch */
arg++; continue; case'(': if (in_quote) continue;
parens++; continue; case')': if (in_quote) continue;
parens--; if (WARN_ONCE(parens < 0, "Paren mismatch for event: %s\narg='%s'\n%*s",
trace_event_name(call),
fmt + start_arg,
(i - start_arg) + 5, "^")) return; continue; case',': if (in_quote || parens) continue;
e = i;
i++; while (isspace(fmt[i]))
i++;
/* * If start_arg is zero, then this is the start of the * first argument. The processing of the argument happens * when the end of the argument is found, as it needs to * handle paranthesis and such.
*/ if (!start_arg) {
start_arg = i; /* Balance out the i++ in the for loop */
i--; continue;
}
if (dereference_flags & (1ULL << arg)) {
handle_dereference_arg(fmt + start_arg, string_flags,
e - start_arg,
&dereference_flags, arg, call);
}
start_arg = i;
arg++; /* Balance out the i++ in the for loop */
i--;
}
}
if (dereference_flags & (1ULL << arg)) {
handle_dereference_arg(fmt + start_arg, string_flags,
i - start_arg,
&dereference_flags, arg, call);
}
/* * If you triggered the below warning, the trace event reported * uses an unsafe dereference pointer %p*. As the data stored * at the trace event time may no longer exist when the trace * event is printed, dereferencing to the original source is * unsafe. The source of the dereference must be copied into the * event itself, and the dereference must access the copy instead.
*/ if (WARN_ON_ONCE(dereference_flags)) {
arg = 1; while (!(dereference_flags & 1)) {
dereference_flags >>= 1;
arg++;
}
pr_warn("event %s has unsafe dereference of argument %d\n",
trace_event_name(call), arg);
pr_warn("print_fmt: %s\n", fmt);
}
}
int trace_event_raw_init(struct trace_event_call *call)
{ int id;
id = register_trace_event(&call->event); if (!id) return -ENODEV;
/* * This is recorded at every sched_switch for this task. * Thus, even if the task migrates the ignore value will be the same.
*/ return this_cpu_read(tr->array_buffer.data->ignore_pid) != 0;
}
EXPORT_SYMBOL_GPL(trace_event_ignore_this_pid);
if ((trace_file->flags & EVENT_FILE_FL_PID_FILTER) &&
trace_event_ignore_this_pid(trace_file)) return NULL;
/* * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables * preemption (adding one to the preempt_count). Since we are * interested in the preempt_count at the time the tracepoint was * hit, we need to subtract one to offset the increment.
*/
fbuffer->trace_ctx = tracing_gen_ctx_dec();
fbuffer->trace_file = trace_file;
WARN_ON(!(call->flags & TRACE_EVENT_FL_TRACEPOINT)); switch (type) { case TRACE_REG_REGISTER: return tracepoint_probe_register(call->tp,
call->class->probe,
file); case TRACE_REG_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->probe,
file); return0;
#ifdef CONFIG_PERF_EVENTS case TRACE_REG_PERF_REGISTER: return tracepoint_probe_register(call->tp,
call->class->perf_probe,
call); case TRACE_REG_PERF_UNREGISTER:
tracepoint_probe_unregister(call->tp,
call->class->perf_probe,
call); return0; case TRACE_REG_PERF_OPEN: case TRACE_REG_PERF_CLOSE: case TRACE_REG_PERF_ADD: case TRACE_REG_PERF_DEL: return0; #endif
} return0;
}
EXPORT_SYMBOL_GPL(trace_event_reg);
staticint __ftrace_event_enable_disable(struct trace_event_file *file, int enable, int soft_disable)
{ struct trace_event_call *call = file->event_call; struct trace_array *tr = file->tr; bool soft_mode = atomic_read(&file->sm_ref) != 0; int ret = 0; int disable;
switch (enable) { case0: /* * When soft_disable is set and enable is cleared, the sm_ref * reference counter is decremented. If it reaches 0, we want * to clear the SOFT_DISABLED flag but leave the event in the * state that it was. That is, if the event was enabled and * SOFT_DISABLED isn't set, then do nothing. But if SOFT_DISABLED * is set we do not want the event to be enabled before we * clear the bit. * * When soft_disable is not set but the soft_mode is, * we do nothing. Do not disable the tracepoint, otherwise * "soft enable"s (clearing the SOFT_DISABLED bit) wont work.
*/ if (soft_disable) { if (atomic_dec_return(&file->sm_ref) > 0) break;
disable = file->flags & EVENT_FILE_FL_SOFT_DISABLED;
soft_mode = false; /* Disable use of trace_buffered_event */
trace_buffered_event_disable();
} else
disable = !soft_mode;
if (disable && (file->flags & EVENT_FILE_FL_ENABLED)) {
clear_bit(EVENT_FILE_FL_ENABLED_BIT, &file->flags); if (file->flags & EVENT_FILE_FL_RECORDED_CMD) {
tracing_stop_cmdline_record();
clear_bit(EVENT_FILE_FL_RECORDED_CMD_BIT, &file->flags);
}
if (file->flags & EVENT_FILE_FL_RECORDED_TGID) {
tracing_stop_tgid_record();
clear_bit(EVENT_FILE_FL_RECORDED_TGID_BIT, &file->flags);
}
ret = call->class->reg(call, TRACE_REG_UNREGISTER, file);
WARN_ON_ONCE(ret);
} /* If in soft mode, just set the SOFT_DISABLE_BIT, else clear it */ if (soft_mode)
set_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags); else
clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags); break; case1: /* * When soft_disable is set and enable is set, we want to * register the tracepoint for the event, but leave the event * as is. That means, if the event was already enabled, we do * nothing (but set soft_mode). If the event is disabled, we * set SOFT_DISABLED before enabling the event tracepoint, so * it still seems to be disabled.
*/ if (!soft_disable)
clear_bit(EVENT_FILE_FL_SOFT_DISABLED_BIT, &file->flags); else { if (atomic_inc_return(&file->sm_ref) > 1) break;
soft_mode = true; /* Enable use of trace_buffered_event */
trace_buffered_event_enable();
}
ret = call->class->reg(call, TRACE_REG_REGISTER, file); if (ret) { if (cmd)
tracing_stop_cmdline_record(); if (tgid)
tracing_stop_tgid_record();
pr_info("event trace: Could not enable event " "%s\n", trace_event_name(call)); break;
}
set_bit(EVENT_FILE_FL_ENABLED_BIT, &file->flags);
/* WAS_ENABLED gets set but never cleared. */
set_bit(EVENT_FILE_FL_WAS_ENABLED_BIT, &file->flags);
} break;
}
return ret;
}
int trace_event_enable_disable(struct trace_event_file *file, int enable, int soft_disable)
{ return __ftrace_event_enable_disable(file, enable, soft_disable);
}
/* * Sched switch is funny, as we only want to ignore it * in the notrace case if both prev and next should be ignored.
*/
ret = trace_ignore_this_task(NULL, no_pid_list, prev) &&
trace_ignore_this_task(NULL, no_pid_list, next);
staticvoid __put_system_dir(struct trace_subsystem_dir *dir)
{
WARN_ON_ONCE(dir->ref_count == 0); /* If the subsystem is about to be freed, the dir must be too */
WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1);
__put_system(dir->subsystem); if (!--dir->ref_count)
kfree(dir);
}
void event_file_put(struct trace_event_file *file)
{ if (WARN_ON_ONCE(!refcount_read(&file->ref))) { if (file->flags & EVENT_FILE_FL_FREED)
kmem_cache_free(file_cachep, file); return;
}
if (refcount_dec_and_test(&file->ref)) { /* Count should only go to zero when it is freed */ if (WARN_ON_ONCE(!(file->flags & EVENT_FILE_FL_FREED))) return;
kmem_cache_free(file_cachep, file);
}
}
/* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events.
*/ staticint
__ftrace_set_clr_event_nolock(struct trace_array *tr, constchar *match, constchar *sub, constchar *event, int set, constchar *mod)
{ struct trace_event_file *file; struct trace_event_call *call; char *module __free(kfree) = NULL; constchar *name; int ret = -EINVAL; int eret = 0;
if (mod) { char *p;
module = kstrdup(mod, GFP_KERNEL); if (!module) return -ENOMEM;
/* Replace all '-' with '_' as that's what modules do */ for (p = strchr(module, '-'); p; p = strchr(p + 1, '-'))
*p = '_';
}
list_for_each_entry(file, &tr->events, list) {
call = file->event_call;
/* If a module is specified, skip events that are not that module */ if (module && (!call->module || strcmp(module_name(call->module), module))) continue;
name = trace_event_name(call);
if (!name || !call->class || !call->class->reg) continue;
if (call->flags & TRACE_EVENT_FL_IGNORE_ENABLE) continue;
if (sub && strcmp(sub, call->class->system) != 0) continue;
if (event && strcmp(event, name) != 0) continue;
ret = ftrace_event_enable_disable(file, set);
/* * Save the first error and return that. Some events * may still have been enabled, but let the user * know that something went wrong.
*/ if (ret && !eret)
eret = ret;
ret = eret;
}
/* * If this is a module setting and nothing was found, * check if the module was loaded. If it wasn't cache it.
*/ if (module && ret == -EINVAL && !eret)
ret = cache_mod(tr, module, set, match, sub, event);
return ret;
}
staticint __ftrace_set_clr_event(struct trace_array *tr, constchar *match, constchar *sub, constchar *event, int set, constchar *mod)
{ int ret;
mutex_lock(&event_mutex);
ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set, mod);
mutex_unlock(&event_mutex);
return ret;
}
int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
{ char *event = NULL, *sub = NULL, *match, *mod; int ret;
if (!tr) return -ENOENT;
/* Modules events can be appened with :mod:<module> */
mod = strstr(buf, ":mod:"); if (mod) {
*mod = '\0'; /* move to the module name */
mod += 5;
}
/* * The buf format can be <subsystem>:<event-name> * *:<event-name> means any event by that name. * :<event-name> is the same. * * <subsystem>:* means all events in that subsystem * <subsystem>: means the same. * * <name> (no ':') means all events in a subsystem with * the name <name> or any event that matches <name>
*/
match = strsep(&buf, ":"); if (buf) {
sub = match;
event = buf;
match = NULL;
if (!strlen(sub) || strcmp(sub, "*") == 0)
sub = NULL; if (!strlen(event) || strcmp(event, "*") == 0)
event = NULL;
} elseif (mod) { /* Allow wildcard for no length or star */ if (!strlen(match) || strcmp(match, "*") == 0)
match = NULL;
}
ret = __ftrace_set_clr_event(tr, match, sub, event, set, mod);
/* Put back the colon to allow this to be called again */ if (buf)
*(buf - 1) = ':';
return ret;
}
/** * trace_set_clr_event - enable or disable an event * @system: system name to match (NULL for any system) * @event: event name to match (NULL for all events, within system) * @set: 1 to enable, 0 to disable * * This is a way for other parts of the kernel to enable or disable * event recording. * * Returns 0 on success, -EINVAL if the parameters do not match any * registered events.
*/ int trace_set_clr_event(constchar *system, constchar *event, int set)
{ struct trace_array *tr = top_trace_array();
/** * trace_array_set_clr_event - enable or disable an event for a trace array. * @tr: concerned trace array. * @system: system name to match (NULL for any system) * @event: event name to match (NULL for all events, within system) * @enable: true to enable, false to disable * * This is a way for other parts of the kernel to enable or disable * event recording. * * Returns 0 on success, -EINVAL if the parameters do not match any * registered events.
*/ int trace_array_set_clr_event(struct trace_array *tr, constchar *system, constchar *event, bool enable)
{ int set;
list_for_each_entry_continue(file, &tr->events, list) {
call = file->event_call; /* * The ftrace subsystem is for showing formats only. * They can not be enabled or disabled via the event files.
*/ if (call->class && call->class->reg &&
!(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) return file;
}
/* * The iter is allocated in s_start() and passed via the 'v' * parameter. To stop the iterator, NULL must be returned. But * the return value is what the 'v' parameter in s_stop() receives * and frees. Free iter here as it will no longer be used.
*/
kfree(iter); return NULL;
}
if (iter->type == SET_EVENT_FILE) return t_show(m, iter->file);
/* When match is set, system and event are not */ if (iter->event_mod->match) {
seq_printf(m, "%s:mod:%s\n", iter->event_mod->match,
iter->event_mod->module); return0;
}
/* * Grab the mutex, to keep calls to p_next() having the same * tr->filtered_pids as p_start() has. * If we just passed the tr->filtered_pids around, then RCU would * have been enough, but doing that makes things more complex.
*/
mutex_lock(&event_mutex);
rcu_read_lock_sched();
ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret;
guard(mutex)(&event_mutex);
switch (val) { case0: case1:
file = event_file_file(filp); if (!file) return -ENODEV;
ret = tracing_update_buffers(file->tr); if (ret < 0) return ret;
ret = ftrace_event_enable_disable(file, val); if (ret < 0) return ret; break;
default: return -EINVAL;
}
*ppos += cnt;
return cnt;
}
/* * Returns: * 0 : no events exist? * 1 : all events are disabled * 2 : all events are enabled * 3 : some events are enabled and some are enabled
*/ int trace_events_enabled(struct trace_array *tr, constchar *system)
{ struct trace_event_call *call; struct trace_event_file *file; int set = 0;
if (system && strcmp(call->class->system, system) != 0) continue;
/* * We need to find out if all the events are set * or if all events or cleared, or if we have * a mixture.
*/
set |= (1 << !!(file->flags & EVENT_FILE_FL_ENABLED));
/* * If we have a mixture, no need to look further.
*/ if (set == 3) break;
}
/* Make sure the system still exists */
mutex_lock(&event_mutex);
mutex_lock(&trace_types_lock);
list_for_each_entry(iter_tr, &ftrace_trace_arrays, list) {
list_for_each_entry(iter_dir, &iter_tr->systems, list) { if (iter_dir == inode->i_private) { /* Don't open systems with no events */
tr = iter_tr;
dir = iter_dir; if (dir->nr_events) {
__get_system_dir(dir);
system = dir->subsystem;
} goto exit_loop;
}
}
}
exit_loop:
mutex_unlock(&trace_types_lock);
mutex_unlock(&event_mutex);
if (!system) return -ENODEV;
/* Still need to increment the ref count of the system */ if (trace_array_get(tr) < 0) {
put_system(dir); return -ENODEV;
}
ret = tracing_open_generic(inode, filp); if (ret < 0) {
trace_array_put(tr);
put_system(dir);
}
/* * If dir->subsystem is NULL, then this is a temporary * descriptor that was made for a trace_array to enable * all subsystems.
*/ if (dir->subsystem)
put_system(dir); else
kfree(dir);
/* * This function is called by on_each_cpu() while the * event_mutex is held.
*/
pid_list = rcu_dereference_protected(tr->filtered_pids,
mutex_is_locked(&event_mutex));
no_pid_list = rcu_dereference_protected(tr->filtered_no_pids,
mutex_is_locked(&event_mutex));
staticvoid register_pid_events(struct trace_array *tr)
{ /* * Register a probe that is called before all other probes * to set ignore_pid if next or prev do not match. * Register a probe this is called after all other probes * to only keep ignore_pid set if next pid matches.
*/
register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_pre,
tr, INT_MAX);
register_trace_prio_sched_switch(event_filter_pid_sched_switch_probe_post,
tr, 0);
/* * Ignoring of pids is done at task switch. But we have to * check for those tasks that are currently running. * Always do this in case a pid was appended or removed.
*/
on_each_cpu(ignore_task_cpu, tr, 1);
/* First see if we did not already create this dir */
list_for_each_entry(dir, &tr->systems, list) {
system = dir->subsystem; if (strcmp(system->name, name) == 0) {
dir->nr_events++;
file->system = dir; return dir->ei;
}
}
/* Now see if the system itself exists. */
system = NULL;
list_for_each_entry(iter, &event_subsystems, list) { if (strcmp(iter->name, name) == 0) {
system = iter; break;
}
}
dir = kmalloc(sizeof(*dir), GFP_KERNEL); if (!dir) goto out_fail;
if (!system) {
system = create_new_subsystem(name); if (!system) goto out_free;
} else
__get_system(system);
/* ftrace only has directories no files */ if (strcmp(name, "ftrace") == 0)
nr_entries = 0; else
nr_entries = ARRAY_SIZE(system_entries);
ei = eventfs_create_dir(name, parent, system_entries, nr_entries, dir); if (IS_ERR(ei)) {
pr_warn("Failed to create system directory %s\n", name);
__put_system(system); goto out_free;
}
out_free:
kfree(dir);
out_fail: /* Only print this message if failed on memory allocation */ if (!dir || !system)
pr_warn("No memory to create event subsystem %s\n", name); return NULL;
}
staticint
event_define_fields(struct trace_event_call *call)
{ struct list_head *head; int ret = 0;
/* * Other events may have the same class. Only update * the fields if they are not already defined.
*/
head = trace_get_fields(call); if (list_empty(head)) { struct trace_event_fields *field = call->class->fields_array; unsignedint offset = sizeof(struct trace_entry);
for (; field->type; field++) { if (field->type == TRACE_FUNCTION_TYPE) {
field->define_fields(call); break;
}
offset = ALIGN(offset, field->align);
ret = trace_define_field_ext(call, field->type, field->name,
offset, field->size,
field->is_signed, field->filter_type,
field->len, field->needs_test); if (WARN_ON_ONCE(ret)) {
pr_err("error code is %d\n", ret); break;
}
/* * Only event directories that can be enabled should have * triggers or filters, with the exception of the "print" * event that can have a "trigger" file.
*/ if (!(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) { if (call->class->reg && strcmp(name, "enable") == 0) {
*mode = TRACE_MODE_WRITE;
*fops = &ftrace_enable_fops; return1;
}
/* The file is incremented on creation and freeing the enable file decrements it */ staticvoid event_release(constchar *name, void *data)
{ struct trace_event_file *file = data;
/* * If the trace point header did not define TRACE_SYSTEM * then the system would be called "TRACE_SYSTEM". This should * never happen.
*/ if (WARN_ON_ONCE(strcmp(call->class->system, TRACE_SYSTEM) == 0)) return -ENODEV;
e_events = event_subsystem_dir(tr, call->class->system, file, parent); if (!e_events) return -ENOMEM;
nr_entries = ARRAY_SIZE(event_entries);
name = trace_event_name(call);
ei = eventfs_create_dir(name, e_events, event_entries, nr_entries, file); if (IS_ERR(ei)) {
pr_warn("Could not create tracefs '%s' directory\n", name); return -1;
}
file->ei = ei;
ret = event_define_fields(call); if (ret < 0) {
pr_warn("Could not initialize trace point events/%s\n", name); return ret;
}
/* Gets decremented on freeing of the "enable" file */
event_file_get(file);
do_for_each_event_file_safe(tr, file) { if (file->event_call != call) continue;
remove_event_file_dir(file); /* * The do_for_each_event_file_safe() is * a double loop. After finding the call for this * trace_array, we use break to jump to the next * trace_array.
*/ break;
} while_for_each_event_file();
}
do_for_each_event_file(tr, file) { if (file->event_call != call) continue;
if (file->flags & EVENT_FILE_FL_WAS_ENABLED)
tr->clear_trace = true;
ftrace_event_enable_disable(file, 0); /* * The do_for_each_event_file() is * a double loop. After finding the call for this * trace_array, we use break to jump to the next * trace_array.
*/ break;
} while_for_each_event_file();
if (call->event.funcs)
__unregister_trace_event(&call->event);
remove_event_from_tracers(call);
list_del(&call->list);
}
staticint event_init(struct trace_event_call *call)
{ int ret = 0; constchar *name;
name = trace_event_name(call); if (WARN_ON(!name)) return -EINVAL;
if (call->class->raw_init) {
ret = call->class->raw_init(call); if (ret < 0 && ret != -ENOSYS)
pr_warn("Could not initialize trace events/%s\n", name);
}
return ret;
}
staticint
__register_event(struct trace_event_call *call, struct module *mod)
{ int ret;
if (call->flags & TRACE_EVENT_FL_DYNAMIC)
atomic_set(&call->refcnt, 0); else
call->module = mod;
return0;
}
staticchar *eval_replace(char *ptr, struct trace_eval_map *map, int len)
{ int rlen; int elen;
/* Find the length of the eval value as a string */
elen = snprintf(ptr, 0, "%ld", map->eval_value); /* Make sure there's enough room to replace the string with the value */ if (len < elen) return NULL;
snprintf(ptr, elen + 1, "%ld", map->eval_value);
/* Get the rest of the string of ptr */
rlen = strlen(ptr + len);
memmove(ptr + elen, ptr + len, rlen); /* Make sure we end the new string */
ptr[elen + rlen] = 0;
return ptr + elen;
}
staticvoid update_event_printk(struct trace_event_call *call, struct trace_eval_map *map)
{ char *ptr; int quote = 0; int len = strlen(map->eval_string);
for (ptr = call->print_fmt; *ptr; ptr++) { if (*ptr == '\\') {
ptr++; /* paranoid */ if (!*ptr) break; continue;
} if (*ptr == '"') {
quote ^= 1; continue;
} if (quote) continue; if (isdigit(*ptr)) { /* skip numbers */ do {
ptr++; /* Check for alpha chars like ULL */
} while (isalnum(*ptr)); if (!*ptr) break; /* * A number must have some kind of delimiter after * it, and we can ignore that too.
*/ continue;
} if (isalpha(*ptr) || *ptr == '_') { if (strncmp(map->eval_string, ptr, len) == 0 &&
!isalnum(ptr[len]) && ptr[len] != '_') {
ptr = eval_replace(ptr, map, len); /* enum/sizeof string smaller than value */ if (WARN_ON_ONCE(!ptr)) return; /* * No need to decrement here, as eval_replace() * returns the pointer to the character passed * the eval, and two evals can not be placed * back to back without something in between. * We can skip that something in between.
*/ continue;
}
skip_more: do {
ptr++;
} while (isalnum(*ptr) || *ptr == '_'); if (!*ptr) break; /* * If what comes after this variable is a '.' or * '->' then we can continue to ignore that string.
*/ if (*ptr == '.' || (ptr[0] == '-' && ptr[1] == '>')) {
ptr += *ptr == '.' ? 1 : 2; if (!*ptr) break; goto skip_more;
} /* * Once again, we can skip the delimiter that came * after the string.
*/ continue;
}
}
}
/* * If we failed to allocate memory here, then we'll just * let the str memory leak when the module is removed. * If this fails to allocate, there's worse problems than * a leaked string on module removal.
*/ if (WARN_ON_ONCE(!modstr)) return;
/* Remove all __attribute__() from @type. Return allocated string or @type. */ staticchar *sanitize_field_type(constchar *type)
{ char *attr, *tmp, *next, *ret = (char *)type; int depth;
next = (char *)type; while ((attr = strstr(next, ATTRIBUTE_STR))) { /* Retry if "__attribute__(" is a part of another word. */ if (attr != next && !isspace(attr[-1])) {
next = attr + ATTRIBUTE_STR_LEN; continue;
}
if (ret == type) {
ret = kstrdup(type, GFP_KERNEL); if (WARN_ON_ONCE(!ret)) return NULL;
attr = ret + (attr - type);
}
/* the ATTRIBUTE_STR already has the first '(' */
depth = 1;
next = attr + ATTRIBUTE_STR_LEN; do {
tmp = strpbrk(next, "()"); /* There is unbalanced parentheses */ if (WARN_ON_ONCE(!tmp)) {
kfree(ret); return (char *)type;
}
if (*tmp == '(')
depth++; else
depth--;
next = tmp + 1;
} while (depth > 0);
next = skip_spaces(next);
strcpy(attr, next);
next = attr;
} return ret;
}
staticchar *find_replacable_eval(constchar *type, constchar *eval_string, int len)
{ char *ptr;
if (!eval_string) return NULL;
ptr = strchr(type, '['); if (!ptr) return NULL;
ptr++;
if (!isalpha(*ptr) && *ptr != '_') return NULL;
if (strncmp(eval_string, ptr, len) != 0) return NULL;
/* Dynamic events should never have field maps */ if (call->flags & TRACE_EVENT_FL_DYNAMIC) return;
if (map) {
eval_string = map->eval_string;
len = strlen(map->eval_string);
}
head = trace_get_fields(call);
list_for_each_entry(field, head, link) {
str = sanitize_field_type(field->type); if (!str) return;
ptr = find_replacable_eval(str, eval_string, len); if (ptr) { if (str == field->type) {
str = kstrdup(field->type, GFP_KERNEL); if (WARN_ON_ONCE(!str)) return;
ptr = str + (ptr - field->type);
}
ptr = eval_replace(ptr, map, len); /* enum/sizeof string smaller than value */ if (WARN_ON_ONCE(!ptr)) {
kfree(str); continue;
}
}
if (str == field->type) continue; /* * If the event is part of a module, then we need to free the string * when the module is removed. Otherwise, it will stay allocated * until a reboot.
*/ if (call->module)
add_str_to_module(call->module, str);
/* Update all events for replacing eval and sanitizing */ void trace_event_update_all(struct trace_eval_map **map, int len)
{ struct trace_event_call *call, *p; constchar *last_system = NULL; bool first = false; bool updated; int last_i; int i;
down_write(&trace_event_sem);
list_for_each_entry_safe(call, p, &ftrace_events, list) { /* events are usually grouped together with systems */ if (!last_system || call->class->system != last_system) {
first = true;
last_i = 0;
last_system = call->class->system;
}
updated = false; /* * Since calls are grouped by systems, the likelihood that the * next call in the iteration belongs to the same system as the * previous call is high. As an optimization, we skip searching * for a map[] that matches the call's system if the last call * was from the same system. That's what last_i is for. If the * call has the same system as the previous call, then last_i * will be the index of the first map[] that has a matching * system.
*/ for (i = last_i; i < len; i++) { if (call->class->system == map[i]->system) { /* Save the first system if need be */ if (first) {
last_i = i;
first = false;
}
update_event_printk(call, map[i]);
update_event_fields(call, map[i]);
updated = true;
}
} /* If not updated yet, update field for sanitizing. */ if (!updated)
update_event_fields(call, NULL);
cond_resched();
}
up_write(&trace_event_sem);
}
#ifdef CONFIG_HIST_TRIGGERS /* * Wake up waiter on the hist_poll_wq from irq_work because the hist trigger * may happen in any context.
*/ staticvoid hist_poll_event_irq_work(struct irq_work *work)
{
wake_up_all(&hist_poll_wq);
}
/* Add an event to a trace directory */ staticint
__trace_add_new_event(struct trace_event_call *call, struct trace_array *tr)
{ struct trace_event_file *file;
file = trace_create_new_event(call, tr); /* * trace_create_new_event() returns ERR_PTR(-ENOMEM) if failed * allocation, or NULL if the event is not part of the tr->system_names. * When the event is not part of the tr->system_names, return zero, not * an error.
*/ if (!file) return0;
if (IS_ERR(file)) return PTR_ERR(file);
if (eventdir_initialized) return event_create_dir(tr->event_dir, file); else return event_define_fields(call);
}
staticvoid trace_early_triggers(struct trace_event_file *file, constchar *name)
{ int ret; int i;
for (i = 0; i < nr_boot_triggers; i++) { if (strcmp(name, bootup_triggers[i].event)) continue;
mutex_lock(&event_mutex);
ret = trigger_process_regex(file, bootup_triggers[i].trigger);
mutex_unlock(&event_mutex); if (ret)
pr_err("Failed to register trigger '%s' on event %s\n",
bootup_triggers[i].trigger,
bootup_triggers[i].event);
}
}
/* * Just create a descriptor for early init. A descriptor is required * for enabling events at boot. We want to enable events before * the filesystem is initialized.
*/ staticint
__trace_early_add_new_event(struct trace_event_call *call, struct trace_array *tr)
{ struct trace_event_file *file; int ret;
file = trace_create_new_event(call, tr); /* * trace_create_new_event() returns ERR_PTR(-ENOMEM) if failed * allocation, or NULL if the event is not part of the tr->system_names. * When the event is not part of the tr->system_names, return zero, not * an error.
*/ if (!file) return0;
if (IS_ERR(file)) return PTR_ERR(file);
ret = event_define_fields(call); if (ret) return ret;
/* * Must be called under locking of trace_types_lock, event_mutex and * trace_event_sem.
*/ staticvoid __trace_remove_event_call(struct trace_event_call *call)
{
event_remove(call);
trace_destroy_fields(call);
}
#ifdef CONFIG_PERF_EVENTS if (call->perf_refcount) return -EBUSY; #endif
do_for_each_event_file(tr, file) { if (file->event_call != call) continue; /* * We can't rely on ftrace_event_enable_disable(enable => 0) * we are going to do, soft mode can suppress * TRACE_REG_UNREGISTER.
*/ if (file->flags & EVENT_FILE_FL_ENABLED) goto busy;
if (file->flags & EVENT_FILE_FL_WAS_ENABLED)
tr->clear_trace = true; /* * The do_for_each_event_file_safe() is * a double loop. After finding the call for this * trace_array, we use break to jump to the next * trace_array.
*/ break;
} while_for_each_event_file();
__trace_remove_event_call(call);
return0;
busy: /* No need to clear the trace now */
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
tr->clear_trace = false;
} return -EBUSY;
}
/* Remove an event_call */ int trace_remove_event_call(struct trace_event_call *call)
{ int ret;
lockdep_assert_held(&event_mutex);
mutex_lock(&trace_types_lock);
down_write(&trace_event_sem);
ret = probe_remove_event_call(call);
up_write(&trace_event_sem);
mutex_unlock(&trace_types_lock);
/* Don't add infrastructure for mods without tracepoints */ if (trace_module_has_bad_taint(mod)) {
pr_err("%s: module has bad taint, not creating trace events\n",
mod->name); return;
}
start = mod->trace_events;
end = mod->trace_events + mod->num_trace_events;
down_write(&trace_event_sem);
list_for_each_entry_safe(call, p, &ftrace_events, list) { if ((call->flags & TRACE_EVENT_FL_DYNAMIC) || !call->module) continue; if (call->module == mod)
__trace_remove_event_call(call);
} /* Check for any strings allocated for this module */
list_for_each_entry_safe(modstr, m, &module_strings, next) { if (modstr->module != mod) continue;
list_del(&modstr->next);
kfree(modstr->str);
kfree(modstr);
}
up_write(&trace_event_sem);
/* * It is safest to reset the ring buffer if the module being unloaded * registered any events that were used. The only worry is if * a new module gets loaded, and takes on the same id as the events * of this module. When printing out the buffer, traced events left * over from this module may be passed to the new module events and * unexpected results may occur.
*/
tracing_reset_all_online_cpus_unlocked();
}
/* Create a new event directory structure for a trace directory. */ staticvoid
__trace_add_event_dirs(struct trace_array *tr)
{ struct trace_event_call *call; int ret;
lockdep_assert_held(&trace_event_sem);
list_for_each_entry(call, &ftrace_events, list) {
ret = __trace_add_new_event(call, tr); if (ret < 0)
pr_warn("Could not create directory for event %s\n",
trace_event_name(call));
}
}
/* Returns any file that matches the system and event */ struct trace_event_file *
__find_event_file(struct trace_array *tr, constchar *system, constchar *event)
{ struct trace_event_file *file; struct trace_event_call *call; constchar *name;
list_for_each_entry(file, &tr->events, list) {
call = file->event_call;
name = trace_event_name(call);
/** * trace_get_event_file - Find and return a trace event file * @instance: The name of the trace instance containing the event * @system: The name of the system containing the event * @event: The name of the event * * Return a trace event file given the trace instance name, trace * system, and trace event name. If the instance name is NULL, it * refers to the top-level trace array. * * This function will look it up and return it if found, after calling * trace_array_get() to prevent the instance from going away, and * increment the event's module refcount to prevent it from being * removed. * * To release the file, call trace_put_event_file(), which will call * trace_array_put() and decrement the event's module refcount. * * Return: The trace event on success, ERR_PTR otherwise.
*/ struct trace_event_file *trace_get_event_file(constchar *instance, constchar *system, constchar *event)
{ struct trace_array *tr = top_trace_array(); struct trace_event_file *file = NULL; int ret = -EINVAL;
if (instance) {
tr = trace_array_find_get(instance); if (!tr) return ERR_PTR(-ENOENT);
} else {
ret = trace_array_get(tr); if (ret) return ERR_PTR(ret);
}
/* Don't let event modules unload while in use */
ret = trace_event_try_get_ref(file->event_call); if (!ret) {
trace_array_put(tr); return ERR_PTR(-EBUSY);
}
/** * trace_put_event_file - Release a file from trace_get_event_file() * @file: The trace event file * * If a file was retrieved using trace_get_event_file(), this should * be called when it's no longer needed. It will cancel the previous * trace_array_get() called by that function, and decrement the * event's module refcount.
*/ void trace_put_event_file(struct trace_event_file *file)
{
mutex_lock(&event_mutex);
trace_event_put_ref(file->event_call);
mutex_unlock(&event_mutex);
ret = register_ftrace_function_probe(glob, tr, ops, data); /* * The above returns on success the # of functions enabled, * but if it didn't find any functions it returns zero. * Consider no functions a failure too.
*/
/* Just return zero, not the number of enabled functions */ if (ret > 0) return0;
static __init int register_event_cmds(void)
{ int ret;
ret = register_ftrace_command(&event_enable_cmd); if (WARN_ON(ret < 0)) return ret;
ret = register_ftrace_command(&event_disable_cmd); if (WARN_ON(ret < 0))
unregister_ftrace_command(&event_enable_cmd); return ret;
} #else staticinlineint register_event_cmds(void) { return0; } #endif/* CONFIG_DYNAMIC_FTRACE */
/* * The top level array and trace arrays created by boot-time tracing * have already had its trace_event_file descriptors created in order * to allow for early events to be recorded. * This function is called after the tracefs has been initialized, * and we now have to create the files associated to the events.
*/ staticvoid __trace_early_add_event_dirs(struct trace_array *tr)
{ struct trace_event_file *file; int ret;
list_for_each_entry(file, &tr->events, list) {
ret = event_create_dir(tr->event_dir, file); if (ret < 0)
pr_warn("Could not create directory for event %s\n",
trace_event_name(file->event_call));
}
}
/* * For early boot up, the top trace array and the trace arrays created * by boot-time tracing require to have a list of events that can be * enabled. This must be done before the filesystem is set up in order * to allow events to be traced early.
*/ void __trace_early_add_events(struct trace_array *tr)
{ struct trace_event_call *call; int ret;
list_for_each_entry(call, &ftrace_events, list) { /* Early boot up should not have any modules loaded */ if (!(call->flags & TRACE_EVENT_FL_DYNAMIC) &&
WARN_ON_ONCE(call->module)) continue;
ret = __trace_early_add_new_event(call, tr); if (ret < 0)
pr_warn("Could not create early event %s\n",
trace_event_name(call));
}
}
/* Remove the event directory structure for a trace directory. */ staticvoid
__trace_remove_event_dirs(struct trace_array *tr)
{ struct trace_event_file *file, *next;
/** * event_trace_add_tracer - add a instance of a trace_array to events * @parent: The parent dentry to place the files/directories for events in * @tr: The trace array associated with these events * * When a new instance is created, it needs to set up its events * directory, as well as other files associated with events. It also * creates the event hierarchy in the @parent/events directory. * * Returns 0 on success. * * Must be called with event_mutex held.
*/ int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr)
{ int ret;
lockdep_assert_held(&event_mutex);
ret = create_event_toplevel_files(parent, tr); if (ret) goto out;
down_write(&trace_event_sem); /* If tr already has the event list, it is initialized in early boot. */ if (unlikely(!list_empty(&tr->events)))
__trace_early_add_event_dirs(tr); else
__trace_add_event_dirs(tr);
up_write(&trace_event_sem);
out: return ret;
}
/* * The top trace array already had its file descriptors created. * Now the files themselves need to be created.
*/ static __init int
early_event_add_tracer(struct dentry *parent, struct trace_array *tr)
{ int ret;
guard(mutex)(&event_mutex);
ret = create_event_toplevel_files(parent, tr); if (ret) return ret;
call = *iter;
ret = event_init(call); if (!ret)
list_add(&call->list, &ftrace_events);
}
register_trigger_cmds();
/* * We need the top trace array to have a working set of trace * points at early init, before the debug files and directories * are created. Create the file entries now, and attach them * to the actual file dentries later.
*/
__trace_early_add_events(tr);
early_enable_events(tr, bootup_event_buf, false);
trace_printk_start_comm();
register_event_cmds();
return0;
}
/* * event_trace_enable() is called from trace_event_init() first to * initialize events and perhaps start any events that are on the * command line. Unfortunately, there are some events that will not * start this early, like the system call tracepoints that need * to set the %SYSCALL_WORK_SYSCALL_TRACEPOINT flag of pid 1. But * event_trace_enable() is called before pid 1 starts, and this flag * is never set, making the syscall tracepoint never get reached, but * the event is enabled regardless (and not doing anything).
*/ static __init int event_trace_enable_again(void)
{ struct trace_array *tr;
tr = top_trace_array(); if (!tr) return -ENODEV;
early_enable_events(tr, bootup_event_buf, true);
return0;
}
early_initcall(event_trace_enable_again);
/* Init fields which doesn't related to the tracefs */ static __init int event_trace_init_fields(void)
{ if (trace_define_generic_fields())
pr_warn("tracing: Failed to allocated generic fields");
if (trace_define_common_fields())
pr_warn("tracing: Failed to allocate common fields");
return0;
}
__init int event_trace_init(void)
{ struct trace_array *tr; int ret;
/* * For every trace event defined, we will test each trace point separately, * and then by groups, and finally all trace points.
*/ static __init void event_trace_self_tests(void)
{ struct trace_subsystem_dir *dir; struct trace_event_file *file; struct trace_event_call *call; struct event_subsystem *system; struct trace_array *tr; int ret;
tr = top_trace_array(); if (!tr) return;
pr_info("Running tests on trace events:\n");
list_for_each_entry(file, &tr->events, list) {
call = file->event_call;
/* Only test those that have a probe */ if (!call->class || !call->class->probe) continue;
/* * Testing syscall events here is pretty useless, but * we still do it if configured. But this is time consuming. * What we really need is a user thread to perform the * syscalls as we test.
*/ #ifndef CONFIG_EVENT_TRACE_TEST_SYSCALLS if (call->class->system &&
strcmp(call->class->system, "syscalls") == 0) continue; #endif
/* * If an event is already enabled, someone is using * it and the self test should not be on.
*/ if (file->flags & EVENT_FILE_FL_ENABLED) {
pr_warn("Enabled event during self test!\n");
WARN_ON_ONCE(1); continue;
}
static __init void event_trace_self_test_with_function(void)
{ int ret;
event_trace_file.tr = top_trace_array(); if (WARN_ON(!event_trace_file.tr)) return;
ret = register_ftrace_function(&trace_ops); if (WARN_ON(ret < 0)) {
pr_info("Failed to enable function tracer for event tests\n"); return;
}
pr_info("Running tests again, along with the function tracer\n");
event_trace_self_tests();
unregister_ftrace_function(&trace_ops);
} #else static __init void event_trace_self_test_with_function(void)
{
} #endif
static __init int event_trace_self_tests_init(void)
{ if (!tracing_selftest_disabled) {
event_trace_self_tests();
event_trace_self_test_with_function();
}
return0;
}
late_initcall(event_trace_self_tests_init);
#endif
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.111 Sekunden
(vorverarbeitet am 2026-06-07)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.