// SPDX-License-Identifier: GPL-2.0
/*
* event probes
*
* Part of this code was copied from kernel/trace/trace_kprobe.c written by
* Masami Hiramatsu <mhiramat@kernel.org>
*
* Copyright (C) 2021, VMware Inc, Steven Rostedt <rostedt@goodmis.org>
* Copyright (C) 2021, VMware Inc, Tzvetomir Stoyanov tz.stoyanov@gmail.com>
*
*/
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ftrace.h>
#include "trace_dynevent.h"
#include "trace_probe.h"
#include "trace_probe_tmpl.h"
#include "trace_probe_kernel.h"
#define EPROBE_EVENT_SYSTEM "eprobes"
struct trace_eprobe {
/* tracepoint system */
const char *event_system;
/* tracepoint event */
const char *event_name;
/* filter string for the tracepoint */
char *filter_str;
struct trace_event_call *event;
struct dyn_event devent;
struct trace_probe tp;
};
struct eprobe_data {
struct trace_event_file *file;
struct trace_eprobe *ep;
};
static int __trace_eprobe_create(int argc, const char *argv[]);
static void trace_event_probe_cleanup(struct trace_eprobe *ep)
{
if (!ep)
return;
trace_probe_cleanup(&ep->tp);
kfree(ep->event_name);
kfree(ep->event_system);
if (ep->event)
trace_event_put_ref(ep->event);
kfree(ep->filter_str);
kfree(ep);
}
static struct trace_eprobe *to_trace_eprobe(struct dyn_event *ev)
{
return container_of(ev, struct trace_eprobe, devent);
}
static int eprobe_dyn_event_create(const char *raw_command)
{
return trace_probe_create(raw_command, __trace_eprobe_create);
}
static int eprobe_dyn_event_show(struct seq_file *m, struct dyn_event *ev)
{
struct trace_eprobe *ep = to_trace_eprobe(ev);
int i;
seq_printf(m, "e:%s/%s", trace_probe_group_name(&ep->tp),
trace_probe_name(&ep->tp));
seq_printf(m, " %s.%s", ep->event_system, ep->event_name);
for (i = 0; i < ep->tp.nr_args; i++)
seq_printf(m, " %s=%s", ep->tp.args[i].name, ep->tp.args[i].comm);
seq_putc(m, '\n');
return 0;
}
static int unregister_trace_eprobe(struct trace_eprobe *ep)
{
/* If other probes are on the event, just unregister eprobe */
if (trace_probe_has_sibling(&ep->tp))
goto unreg;
/* Enabled event can not be unregistered */
if (trace_probe_is_enabled(&ep->tp))
return -EBUSY;
/* Will fail if probe is being used by ftrace or perf */
if (trace_probe_unregister_event_call(&ep->tp))
return -EBUSY;
unreg:
dyn_event_remove(&ep->devent);
trace_probe_unlink(&ep->tp);
return 0;
}
static int eprobe_dyn_event_release(struct dyn_event *ev)
{
struct trace_eprobe *ep = to_trace_eprobe(ev);
int ret = unregister_trace_eprobe(ep);
if (!ret)
trace_event_probe_cleanup(ep);
return ret;
}
static bool eprobe_dyn_event_is_busy(struct dyn_event *ev)
{
struct trace_eprobe *ep = to_trace_eprobe(ev);
return trace_probe_is_enabled(&ep->tp);
}
static bool eprobe_dyn_event_match(const char *system, const char *event,
int argc, const char **argv, struct dyn_event *ev)
{
struct trace_eprobe *ep = to_trace_eprobe(ev);
const char *slash;
/*
* We match the following:
* event only - match all eprobes with event name
* system and event only - match all system/event probes
* system only - match all system probes
*
* The below has the above satisfied with more arguments:
*
* attached system/event - If the arg has the system and event
* the probe is attached to, match
* probes with the attachment.
*
* If any more args are given, then it requires a full match.
*/
/*
* If system exists, but this probe is not part of that system
* do not match.
*/
if (system && strcmp(trace_probe_group_name(&ep->tp), system) != 0)
return false;
/* Must match the event name */
if (event[0] != '\0' && strcmp(trace_probe_name(&ep->tp), event) != 0)
return false;
/* No arguments match all */
if (argc < 1)
return true;
/* First argument is the system/event the probe is attached to */
slash = strchr(argv[0], '/');
if (!slash)
slash = strchr(argv[0], '.');
if (!slash)
return false;
if (strncmp(ep->event_system, argv[0], slash - argv[0]))
return false;
if (strcmp(ep->event_name, slash + 1))
return false;
argc--;
argv++;
/* If there are no other args, then match */
if (argc < 1)
return true;
return trace_probe_match_command_args(&ep->tp,