tracing/boot: Add support for eprobe, fprobe, and tprobe events

Boot-time tracing currently supports kprobe-events and synthetic-events
under per-event configuration options.

Extend boot-time tracing to support newly added dynamic probe types:
- event probes (eprobe) under the "eprobes" event group
- function probes (fprobe) under the "fprobes" event group
- tracepoint probes (tprobe) under the "tracepoints" or "tprobes"
  event group

To support this cleanly, update dyn_event_create() in trace_dynevent.c
so that passing NULL as the type parameter delegates to
create_dyn_event(), allowing generic creation of any registered
dynamic event type from a raw command string.

Update Documentation/trace/boottime-trace.rst accordingly to describe
the new per-event bootconfig options.

Link: https://lore.kernel.org/all/178613905149.259829.18185480460810689421.stgit@devnote2/

Assisted-by: Antigravity:gemini-3.6-flash
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Changes in v3:
 - Check return values of strscpy() and snprintf() in trace_boot_add_probe_event()
   to prevent silent buffer truncation when constructing probe event strings.

Changes in v2:
 - Fix raw command detection logic for eprobes, fprobes, and tprobes
   by requiring ':' or isspace() after type prefix.
 - Consolidate duplicate loop logic into trace_boot_add_probe_event()
   helper function.
This commit is contained in:
Masami Hiramatsu (Google)
2026-08-08 06:44:11 +09:00
parent c13fcd3951
commit d093d4524b
3 changed files with 117 additions and 3 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

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