1078 lines · 24.5 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;}; 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 trace_eprobe_tp_arg_update(struct trace_eprobe *ep, int i){	struct probe_arg *parg = &ep->tp.args[i];	struct ftrace_event_field *field;	struct list_head *head;	int ret = -ENOENT; 	head = trace_get_fields(ep->event);	list_for_each_entry(field, head, link) {		if (!strcmp(parg->code->data, field->name)) {			kfree(parg->code->data);			parg->code->data = field;			return 0;		}	} 	/*	 * Argument not found on event. But allow for comm and COMM	 * to be used to get the current->comm.	 */	if (strcmp(parg->code->data, "COMM") == 0 ||	    strcmp(parg->code->data, "comm") == 0) {		parg->code->op = FETCH_OP_COMM;		ret = 0;	} 	kfree(parg->code->data);	parg->code->data = NULL;	return 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 (print_probe_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 unsigned long get_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_OP_IMM:				val = code->immediate;				break;			case FETCH_OP_COMM:				val = (unsigned long)current->comm;				break;			case FETCH_OP_DATA:				val = (unsigned long)code->data;				break;			case FETCH_NOP_SYMBOL:	/* Ignore a place holder */				code++;				goto retry;			default:				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;  retry:	switch (code->op) {	case FETCH_OP_TP_ARG:		val = get_event_field(code, rec);		break;	case FETCH_OP_IMM:		val = code->immediate;		break;	case FETCH_OP_COMM:		val = (unsigned long)current->comm;		break;	case FETCH_OP_DATA:		val = (unsigned long)code->data;		break;	case FETCH_NOP_SYMBOL:	/* Ignore a place holder */		code++;		goto retry;	default:		return -EILSEQ;	}	code++;	return process_fetch_insn_bottom(code, val, dest, base);}NOKPROBE_SYMBOL(process_fetch_insn) /* Return the length of string -- including null terminal byte */static nokprobe_inline intfetch_store_strlen_user(unsigned long addr){	return kern_fetch_store_strlen_user(addr);} /* Return the length of string -- including null terminal byte */static nokprobe_inline intfetch_store_strlen(unsigned long addr){	return kern_fetch_store_strlen(addr);} /* * Fetch a null-terminated string from user. Caller MUST set *(u32 *)buf * with max length and relative data location. */static nokprobe_inline intfetch_store_string_user(unsigned long addr, void *dest, void *base){	return kern_fetch_store_string_user(addr, dest, base);} /* * Fetch a null-terminated string. Caller MUST set *(u32 *)buf with max * length and relative data location. */static nokprobe_inline intfetch_store_string(unsigned long addr, void *dest, void *base){	return kern_fetch_store_string(addr, dest, base);} static nokprobe_inline intprobe_mem_read_user(void *dest, void *src, size_t size){	const void __user *uaddr =  (__force const void __user *)src; 	return copy_from_user_nofault(dest, uaddr, size);} static nokprobe_inline intprobe_mem_read(void *dest, void *src, size_t size)

Lines 1–500 of 1078.