2282 lines · 53.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Common code for probe-based Dynamic events. * * This code was copied from kernel/trace/trace_kprobe.c written by * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> * * Updates to make this generic: * Copyright (C) IBM Corporation, 2010-2011 * Author:     Srikar Dronamraju */#define pr_fmt(fmt)	"trace_probe: " fmt #include <linux/bpf.h>#include <linux/fs.h> #include "trace_btf.h"#include "trace_probe.h" #undef C#define C(a, b)		b static const char *trace_probe_err_text[] = { ERRORS }; static const char *reserved_field_names[] = {	"common_type",	"common_flags",	"common_preempt_count",	"common_pid",	"common_tgid",	FIELD_STRING_IP,	FIELD_STRING_RETIP,	FIELD_STRING_FUNC,}; /* Printing  in basic type function template */#define DEFINE_BASIC_PRINT_TYPE_FUNC(tname, type, fmt)			\int PRINT_TYPE_FUNC_NAME(tname)(struct trace_seq *s, void *data, void *ent)\{									\	trace_seq_printf(s, fmt, *(type *)data);			\	return !trace_seq_has_overflowed(s);				\}									\const char PRINT_TYPE_FMT_NAME(tname)[] = fmt; DEFINE_BASIC_PRINT_TYPE_FUNC(u8,  u8,  "%u")DEFINE_BASIC_PRINT_TYPE_FUNC(u16, u16, "%u")DEFINE_BASIC_PRINT_TYPE_FUNC(u32, u32, "%u")DEFINE_BASIC_PRINT_TYPE_FUNC(u64, u64, "%Lu")DEFINE_BASIC_PRINT_TYPE_FUNC(s8,  s8,  "%d")DEFINE_BASIC_PRINT_TYPE_FUNC(s16, s16, "%d")DEFINE_BASIC_PRINT_TYPE_FUNC(s32, s32, "%d")DEFINE_BASIC_PRINT_TYPE_FUNC(s64, s64, "%Ld")DEFINE_BASIC_PRINT_TYPE_FUNC(x8,  u8,  "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC(x16, u16, "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC(x32, u32, "0x%x")DEFINE_BASIC_PRINT_TYPE_FUNC(x64, u64, "0x%Lx")DEFINE_BASIC_PRINT_TYPE_FUNC(char, u8, "'%c'") int PRINT_TYPE_FUNC_NAME(symbol)(struct trace_seq *s, void *data, void *ent){	trace_seq_printf(s, "%pS", (void *)*(unsigned long *)data);	return !trace_seq_has_overflowed(s);}const char PRINT_TYPE_FMT_NAME(symbol)[] = "%pS"; /* Print type function for string type */int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s, void *data, void *ent){	int len = *(u32 *)data >> 16; 	if (!len)		trace_seq_puts(s, FAULT_STRING);	else		trace_seq_printf(s, "\"%s\"",				 (const char *)get_loc_data(data, ent));	return !trace_seq_has_overflowed(s);} const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; /* Fetch type information table */static const struct fetch_type probe_fetch_types[] = {	/* Special types */	__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1,			    "__data_loc char[]"),	__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1,			    "__data_loc char[]"),	__ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1,			    "__data_loc char[]"),	/* Basic types */	ASSIGN_FETCH_TYPE(u8,  u8,  0),	ASSIGN_FETCH_TYPE(u16, u16, 0),	ASSIGN_FETCH_TYPE(u32, u32, 0),	ASSIGN_FETCH_TYPE(u64, u64, 0),	ASSIGN_FETCH_TYPE(s8,  u8,  1),	ASSIGN_FETCH_TYPE(s16, u16, 1),	ASSIGN_FETCH_TYPE(s32, u32, 1),	ASSIGN_FETCH_TYPE(s64, u64, 1),	ASSIGN_FETCH_TYPE_ALIAS(x8,  u8,  u8,  0),	ASSIGN_FETCH_TYPE_ALIAS(x16, u16, u16, 0),	ASSIGN_FETCH_TYPE_ALIAS(x32, u32, u32, 0),	ASSIGN_FETCH_TYPE_ALIAS(x64, u64, u64, 0),	ASSIGN_FETCH_TYPE_ALIAS(char, u8, u8,  0),	ASSIGN_FETCH_TYPE_ALIAS(symbol, ADDR_FETCH_TYPE, ADDR_FETCH_TYPE, 0), 	ASSIGN_FETCH_TYPE_END}; static const struct fetch_type *find_fetch_type(const char *type, unsigned long flags){	int i; 	/* Reject the symbol/symstr for uprobes */	if (type && (flags & TPARG_FL_USER) &&	    (!strcmp(type, "symbol") || !strcmp(type, "symstr")))		return NULL; 	if (!type)		type = DEFAULT_FETCH_TYPE_STR; 	/* Special case: bitfield */	if (*type == 'b') {		unsigned long bs; 		type = strchr(type, '/');		if (!type)			goto fail; 		type++;		if (kstrtoul(type, 0, &bs))			goto fail; 		switch (bs) {		case 8:			return find_fetch_type("u8", flags);		case 16:			return find_fetch_type("u16", flags);		case 32:			return find_fetch_type("u32", flags);		case 64:			return find_fetch_type("u64", flags);		default:			goto fail;		}	} 	for (i = 0; probe_fetch_types[i].name; i++) {		if (strcmp(type, probe_fetch_types[i].name) == 0)			return &probe_fetch_types[i];	} fail:	return NULL;} static struct trace_probe_log trace_probe_log;extern struct mutex dyn_event_ops_mutex; const char *trace_probe_log_init(const char *subsystem, int argc, const char **argv){	lockdep_assert_held(&dyn_event_ops_mutex); 	trace_probe_log.subsystem = subsystem;	trace_probe_log.argc = argc;	trace_probe_log.argv = argv;	trace_probe_log.index = 0;	return subsystem;} void trace_probe_log_clear(void){	lockdep_assert_held(&dyn_event_ops_mutex); 	memset(&trace_probe_log, 0, sizeof(trace_probe_log));} void trace_probe_log_set_index(int index){	lockdep_assert_held(&dyn_event_ops_mutex); 	trace_probe_log.index = index;} void __trace_probe_log_err(int offset, int err_type){	char *command, *p;	int i, len = 0, pos = 0; 	lockdep_assert_held(&dyn_event_ops_mutex); 	if (!trace_probe_log.argv)		return; 	/* Recalculate the length and allocate buffer */	for (i = 0; i < trace_probe_log.argc; i++) {		if (i == trace_probe_log.index)			pos = len;		len += strlen(trace_probe_log.argv[i]) + 1;	}	command = kzalloc(len, GFP_KERNEL);	if (!command)		return; 	if (trace_probe_log.index >= trace_probe_log.argc) {		/**		 * Set the error position is next to the last arg + space.		 * Note that len includes the terminal null and the cursor		 * appears at pos + 1.		 */		pos = len;		offset = 0;	} 	/* And make a command string from argv array */	p = command;	for (i = 0; i < trace_probe_log.argc; i++) {		len = strlen(trace_probe_log.argv[i]);		memcpy(p, trace_probe_log.argv[i], len);		p[len] = ' ';		p += len + 1;	}	*(p - 1) = '\0'; 	tracing_log_err(NULL, trace_probe_log.subsystem, command,			trace_probe_err_text, err_type, pos + offset); 	kfree(command);} /* Split symbol and offset. */int traceprobe_split_symbol_offset(char *symbol, long *offset){	char *tmp;	int ret; 	if (!offset)		return -EINVAL; 	tmp = strpbrk(symbol, "+-");	if (tmp) {		ret = kstrtol(tmp, 0, offset);		if (ret)			return ret;		*tmp = '\0';	} else		*offset = 0; 	return 0;} /** * traceprobe_parse_event_name() - Parse a string into group and event names * @pevent: A pointer to the string to be parsed. * @pgroup: A pointer to the group name. * @buf:    A buffer to store the parsed group name. * @offset: The offset of the string in the original user command, for logging. * * This parses a string with the format `[GROUP/][EVENT]` or `[GROUP.][EVENT]` * (either GROUP or EVENT or both must be specified). * Since the parsed group name is stored in @buf, the caller must ensure @buf * is at least MAX_EVENT_NAME_LEN bytes. * * Return: 0 on success, or -EINVAL on failure. * * If success, *@pevent is updated to point to the event name part of the * original string, or NULL if there is no event name. * Also, *@pgroup is updated to point to the parsed group which is stored * in @buf, or NULL if there is no group name. */int traceprobe_parse_event_name(const char **pevent, const char **pgroup,				char *buf, int offset){	const char *slash, *event = *pevent;	int len; 	slash = strchr(event, '/');	if (!slash)		slash = strchr(event, '.'); 	if (slash) {		if (slash == event) {			trace_probe_log_err(offset, NO_GROUP_NAME);			return -EINVAL;		}		if (slash - event + 1 > MAX_EVENT_NAME_LEN) {			trace_probe_log_err(offset, GROUP_TOO_LONG);			return -EINVAL;		}		strscpy(buf, event, slash - event + 1);		if (!is_good_system_name(buf)) {			trace_probe_log_err(offset, BAD_GROUP_NAME);			return -EINVAL;		}		*pgroup = buf;		*pevent = slash + 1;		offset += slash - event + 1;		event = *pevent;	}	len = strlen(event);	if (len == 0) {		if (slash) {			*pevent = NULL;			return 0;		}		trace_probe_log_err(offset, NO_EVENT_NAME);		return -EINVAL;	} else if (len >= MAX_EVENT_NAME_LEN) {		trace_probe_log_err(offset, EVENT_TOO_LONG);		return -EINVAL;	}	if (!is_good_name(event)) {		trace_probe_log_err(offset, BAD_EVENT_NAME);		return -EINVAL;	}	return 0;} static int parse_trace_event_arg(char *arg, struct fetch_insn *code,				 struct traceprobe_parse_context *ctx){	struct ftrace_event_field *field;	struct list_head *head; 	head = trace_get_fields(ctx->event);	list_for_each_entry(field, head, link) {		if (!strcmp(arg, field->name)) {			code->op = FETCH_OP_TP_ARG;			code->data = field;			return 0;		}	}	return -ENOENT;} #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS static u32 btf_type_int(const struct btf_type *t){	return *(u32 *)(t + 1);} static bool btf_type_is_char_ptr(struct btf *btf, const struct btf_type *type){	const struct btf_type *real_type;	u32 intdata;	s32 tid; 	real_type = btf_type_skip_modifiers(btf, type->type, &tid);	if (!real_type)		return false; 	if (BTF_INFO_KIND(real_type->info) != BTF_KIND_INT)		return false; 	intdata = btf_type_int(real_type);	return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED)		&& BTF_INT_BITS(intdata) == 8;} static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type){	const struct btf_type *real_type;	const struct btf_array *array;	u32 intdata;	s32 tid; 	if (BTF_INFO_KIND(type->info) != BTF_KIND_ARRAY)		return false; 	array = (const struct btf_array *)(type + 1); 	real_type = btf_type_skip_modifiers(btf, array->type, &tid); 	intdata = btf_type_int(real_type);	return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED)		&& BTF_INT_BITS(intdata) == 8;} static int check_prepare_btf_string_fetch(char *typename,				struct fetch_insn **pcode,				struct traceprobe_parse_context *ctx){	struct btf *btf = ctx->btf; 	if (!btf || !ctx->last_type)		return 0; 	/* char [] does not need any change. */	if (btf_type_is_char_array(btf, ctx->last_type))		return 0; 	/* char * requires dereference the pointer. */	if (btf_type_is_char_ptr(btf, ctx->last_type)) {		struct fetch_insn *code = *pcode + 1; 		if (code->op == FETCH_OP_END) {			trace_probe_log_err(ctx->offset, TOO_MANY_OPS);			return -E2BIG;		}		if (typename[0] == 'u')			code->op = FETCH_OP_UDEREF;		else			code->op = FETCH_OP_DEREF;		code->offset = 0;		*pcode = code;		return 0;	}	/* Other types are not available for string */	trace_probe_log_err(ctx->offset, BAD_TYPE4STR);	return -EINVAL;} static const char *fetch_type_from_btf_type(struct btf *btf,					const struct btf_type *type,					struct traceprobe_parse_context *ctx){	u32 intdata; 	/* TODO: const char * could be converted as a string */	switch (BTF_INFO_KIND(type->info)) {	case BTF_KIND_ENUM:		/* enum is "int", so convert to "s32" */		return "s32";	case BTF_KIND_ENUM64:		return "s64";	case BTF_KIND_PTR:		/* pointer will be converted to "x??" */		if (IS_ENABLED(CONFIG_64BIT))			return "x64";		else			return "x32";	case BTF_KIND_INT:		intdata = btf_type_int(type);		if (BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) {			switch (BTF_INT_BITS(intdata)) {			case 8:				return "s8";			case 16:				return "s16";			case 32:				return "s32";			case 64:				return "s64";			}		} else {	/* unsigned */			switch (BTF_INT_BITS(intdata)) {			case 8:				return "u8";			case 16:				return "u16";			case 32:				return "u32";			case 64:				return "u64";			}			/* bitfield, size is encoded in the type */			ctx->last_bitsize = BTF_INT_BITS(intdata);			ctx->last_bitoffs += BTF_INT_OFFSET(intdata);			return "u64";		}	}	/* TODO: support other types */ 	return NULL;} static int query_btf_context(struct traceprobe_parse_context *ctx){	const struct btf_param *param;	const struct btf_type *type;	struct btf *btf;	s32 nr; 	if (ctx->btf)		return 0; 	if (!ctx->funcname)		return -EINVAL; 	type = btf_find_func_proto(ctx->funcname, &btf);	if (!type)		return -ENOENT; 	ctx->btf = btf;	ctx->proto = type; 	/* ctx->params is optional, since func(void) will not have params. */	nr = 0;	param = btf_get_func_param(type, &nr);	if (!IS_ERR_OR_NULL(param)) {		/* Hide the first 'data' argument of tracepoint */		if (ctx->flags & TPARG_FL_TPOINT) {			nr--;			param++;		}	} 	if (nr > 0) {		ctx->nr_params = nr;		ctx->params = param;

Lines 1–500 of 2282.