984 lines · 22.3 KB · C
RawDownload
// 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;};  #define for_each_trace_eprobe_tp(ep, _tp) \	list_for_each_entry(ep, trace_probe_probe_list(_tp), tp.list) 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, argc, argv);} static struct dyn_event_operations eprobe_dyn_event_ops = {	.create = eprobe_dyn_event_create,	.show = eprobe_dyn_event_show,	.is_busy = eprobe_dyn_event_is_busy,	.free = eprobe_dyn_event_release,	.match = eprobe_dyn_event_match,}; static struct trace_eprobe *alloc_event_probe(const char *group,					      const char *this_event,					      struct trace_event_call *event,					      int nargs){	struct trace_eprobe *ep;	const char *event_name;	const char *sys_name;	int ret = -ENOMEM; 	if (!event)		return ERR_PTR(-ENODEV); 	sys_name = event->class->system;	event_name = trace_event_name(event); 	ep = kzalloc(struct_size(ep, tp.args, nargs), GFP_KERNEL);	if (!ep) {		trace_event_put_ref(event);		goto error;	}	ep->event = event;	ep->event_name = kstrdup(event_name, GFP_KERNEL);	if (!ep->event_name)		goto error;	ep->event_system = kstrdup(sys_name, GFP_KERNEL);	if (!ep->event_system)		goto error; 	ret = trace_probe_init(&ep->tp, this_event, group, false);	if (ret < 0)		goto error; 	dyn_event_init(&ep->devent, &eprobe_dyn_event_ops);	return ep;error:	trace_event_probe_cleanup(ep);	return ERR_PTR(ret);} static int eprobe_event_define_fields(struct trace_event_call *event_call){	struct eprobe_trace_entry_head field;	struct trace_probe *tp; 	tp = trace_probe_primary_from_call(event_call);	if (WARN_ON_ONCE(!tp))		return -ENOENT; 	return traceprobe_define_arg_fields(event_call, sizeof(field), tp);} static struct trace_event_fields eprobe_fields_array[] = {	{ .type = TRACE_FUNCTION_TYPE,	  .define_fields = eprobe_event_define_fields },	{}}; /* Event entry printers */static enum print_line_tprint_eprobe_event(struct trace_iterator *iter, int flags,		   struct trace_event *event){	struct eprobe_trace_entry_head *field;	struct trace_event_call *pevent;	struct trace_event *probed_event;	struct trace_seq *s = &iter->seq;	struct trace_eprobe *ep;	struct trace_probe *tp;	unsigned int type; 	field = (struct eprobe_trace_entry_head *)iter->ent;	tp = trace_probe_primary_from_call(		container_of(event, struct trace_event_call, event));	if (WARN_ON_ONCE(!tp))		goto out; 	ep = container_of(tp, struct trace_eprobe, tp);	type = ep->event->event.type; 	trace_seq_printf(s, "%s: (", trace_probe_name(tp)); 	probed_event = ftrace_find_event(type);	if (probed_event) {		pevent = container_of(probed_event, struct trace_event_call, event);		trace_seq_printf(s, "%s.%s", pevent->class->system,				 trace_event_name(pevent));	} else {		trace_seq_printf(s, "%u", type);	} 	trace_seq_putc(s, ')'); 	if (trace_probe_print_args(s, tp->args, tp->nr_args,			     (u8 *)&field[1], field) < 0)		goto out; 	trace_seq_putc(s, '\n'); out:	return trace_handle_return(s);} static nokprobe_inline unsigned longget_event_field(struct fetch_insn *code, void *rec){	struct ftrace_event_field *field = code->data;	unsigned long val;	void *addr; 	addr = rec + field->offset; 	if (is_string_field(field)) {		switch (field->filter_type) {		case FILTER_DYN_STRING:			val = (unsigned long)(rec + (*(unsigned int *)addr & 0xffff));			break;		case FILTER_RDYN_STRING:			val = (unsigned long)(addr + (*(unsigned int *)addr & 0xffff));			break;		case FILTER_STATIC_STRING:			val = (unsigned long)addr;			break;		case FILTER_PTR_STRING:			val = (unsigned long)(*(char *)addr);			break;		default:			WARN_ON_ONCE(1);			return 0;		}		return val;	} 	switch (field->size) {	case 1:		if (field->is_signed)			val = *(char *)addr;		else			val = *(unsigned char *)addr;		break;	case 2:		if (field->is_signed)			val = *(short *)addr;		else			val = *(unsigned short *)addr;		break;	case 4:		if (field->is_signed)			val = *(int *)addr;		else			val = *(unsigned int *)addr;		break;	default:		if (field->is_signed)			val = *(long *)addr;		else			val = *(unsigned long *)addr;		break;	}	return val;} static int get_eprobe_size(struct trace_probe *tp, void *rec){	struct fetch_insn *code;	struct probe_arg *arg;	int i, len, ret = 0; 	for (i = 0; i < tp->nr_args; i++) {		arg = tp->args + i;		if (arg->dynamic) {			unsigned long val; 			code = arg->code; retry:			switch (code->op) {			case FETCH_OP_TP_ARG:				val = get_event_field(code, rec);				break;			case FETCH_NOP_SYMBOL:	/* Ignore a place holder */				code++;				goto retry;			default:				if (process_common_fetch_insn(code, &val) < 0)					continue;			}			code++;			len = process_fetch_insn_bottom(code, val, NULL, NULL);			if (len > 0)				ret += len;		}	} 	return ret;} /* Kprobe specific fetch functions */ /* Note that we don't verify it, since the code does not come from user space */static intprocess_fetch_insn(struct fetch_insn *code, void *rec, void *dest,		   void *base){	unsigned long val;	int ret;  retry:	switch (code->op) {	case FETCH_OP_TP_ARG:		val = get_event_field(code, rec);		break;	case FETCH_NOP_SYMBOL:	/* Ignore a place holder */		code++;		goto retry;	default:		ret = process_common_fetch_insn(code, &val);		if (ret < 0)			return ret;	}	code++;	return process_fetch_insn_bottom(code, val, dest, base);}NOKPROBE_SYMBOL(process_fetch_insn) /* eprobe handler */static inline void__eprobe_trace_func(struct eprobe_data *edata, void *rec){	struct eprobe_trace_entry_head *entry;	struct trace_event_call *call = trace_probe_event_call(&edata->ep->tp);	struct trace_event_buffer fbuffer;	int dsize; 	if (WARN_ON_ONCE(call != edata->file->event_call))		return; 	if (trace_trigger_soft_disabled(edata->file))		return; 	dsize = get_eprobe_size(&edata->ep->tp, rec); 	entry = trace_event_buffer_reserve(&fbuffer, edata->file,					   sizeof(*entry) + edata->ep->tp.size + dsize); 	if (!entry)		return; 	entry = fbuffer.entry = ring_buffer_event_data(fbuffer.event);	store_trace_args(&entry[1], &edata->ep->tp, rec, sizeof(*entry), dsize); 	trace_event_buffer_commit(&fbuffer);} /* * The event probe implementation uses event triggers to get access to * the event it is attached to, but is not an actual trigger. The below * functions are just stubs to fulfill what is needed to use the trigger * infrastructure. */static int eprobe_trigger_init(struct event_trigger_data *data){	return 0;} static void eprobe_trigger_free(struct event_trigger_data *data){ } static int eprobe_trigger_print(struct seq_file *m,				struct event_trigger_data *data){	/* Do not print eprobe event triggers */	return 0;} static void eprobe_trigger_func(struct event_trigger_data *data,				struct trace_buffer *buffer, void *rec,				struct ring_buffer_event *rbe){	struct eprobe_data *edata = data->private_data; 	if (unlikely(!rec))		return; 	__eprobe_trace_func(edata, rec);} static struct event_trigger_ops eprobe_trigger_ops = {	.trigger		= eprobe_trigger_func,	.print			= eprobe_trigger_print,	.init			= eprobe_trigger_init,	.free			= eprobe_trigger_free,}; static int eprobe_trigger_cmd_parse(struct event_command *cmd_ops,				    struct trace_event_file *file,				    char *glob, char *cmd,				    char *param_and_filter){	return -1;} static int eprobe_trigger_reg_func(char *glob,				   struct event_trigger_data *data,				   struct trace_event_file *file){	return -1;

Lines 1–500 of 984.