diff options
Diffstat (limited to 'kernel/trace/trace_probe.h')
| -rw-r--r-- | kernel/trace/trace_probe.h | 242 | 
1 files changed, 241 insertions, 1 deletions
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 5c7e09d10d7..4f815fbce16 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -81,6 +81,17 @@   */  #define convert_rloc_to_loc(dl, offs)	((u32)(dl) + (offs)) +static nokprobe_inline void *get_rloc_data(u32 *dl) +{ +	return (u8 *)dl + get_rloc_offs(*dl); +} + +/* For data_loc conversion */ +static nokprobe_inline void *get_loc_data(u32 *dl, void *ent) +{ +	return (u8 *)ent + get_rloc_offs(*dl); +} +  /* Data fetch function type */  typedef	void (*fetch_func_t)(struct pt_regs *, void *, void *);  /* Printing function type */ @@ -95,6 +106,7 @@ enum {  	FETCH_MTD_symbol,  	FETCH_MTD_deref,  	FETCH_MTD_bitfield, +	FETCH_MTD_file_offset,  	FETCH_MTD_END,  }; @@ -115,6 +127,147 @@ struct fetch_param {  	void 			*data;  }; +/* For defining macros, define string/string_size types */ +typedef u32 string; +typedef u32 string_size; + +#define PRINT_TYPE_FUNC_NAME(type)	print_type_##type +#define PRINT_TYPE_FMT_NAME(type)	print_type_format_##type + +/* Printing  in basic type function template */ +#define DECLARE_BASIC_PRINT_TYPE_FUNC(type)				\ +int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s, const char *name,	\ +				void *data, void *ent);			\ +extern const char PRINT_TYPE_FMT_NAME(type)[] + +DECLARE_BASIC_PRINT_TYPE_FUNC(u8); +DECLARE_BASIC_PRINT_TYPE_FUNC(u16); +DECLARE_BASIC_PRINT_TYPE_FUNC(u32); +DECLARE_BASIC_PRINT_TYPE_FUNC(u64); +DECLARE_BASIC_PRINT_TYPE_FUNC(s8); +DECLARE_BASIC_PRINT_TYPE_FUNC(s16); +DECLARE_BASIC_PRINT_TYPE_FUNC(s32); +DECLARE_BASIC_PRINT_TYPE_FUNC(s64); +DECLARE_BASIC_PRINT_TYPE_FUNC(string); + +#define FETCH_FUNC_NAME(method, type)	fetch_##method##_##type + +/* Declare macro for basic types */ +#define DECLARE_FETCH_FUNC(method, type)				\ +extern void FETCH_FUNC_NAME(method, type)(struct pt_regs *regs, 	\ +					  void *data, void *dest) + +#define DECLARE_BASIC_FETCH_FUNCS(method) 	\ +DECLARE_FETCH_FUNC(method, u8);			\ +DECLARE_FETCH_FUNC(method, u16);		\ +DECLARE_FETCH_FUNC(method, u32);		\ +DECLARE_FETCH_FUNC(method, u64) + +DECLARE_BASIC_FETCH_FUNCS(reg); +#define fetch_reg_string			NULL +#define fetch_reg_string_size			NULL + +DECLARE_BASIC_FETCH_FUNCS(retval); +#define fetch_retval_string			NULL +#define fetch_retval_string_size		NULL + +DECLARE_BASIC_FETCH_FUNCS(symbol); +DECLARE_FETCH_FUNC(symbol, string); +DECLARE_FETCH_FUNC(symbol, string_size); + +DECLARE_BASIC_FETCH_FUNCS(deref); +DECLARE_FETCH_FUNC(deref, string); +DECLARE_FETCH_FUNC(deref, string_size); + +DECLARE_BASIC_FETCH_FUNCS(bitfield); +#define fetch_bitfield_string			NULL +#define fetch_bitfield_string_size		NULL + +/* + * Define macro for basic types - we don't need to define s* types, because + * we have to care only about bitwidth at recording time. + */ +#define DEFINE_BASIC_FETCH_FUNCS(method) \ +DEFINE_FETCH_##method(u8)		\ +DEFINE_FETCH_##method(u16)		\ +DEFINE_FETCH_##method(u32)		\ +DEFINE_FETCH_##method(u64) + +/* Default (unsigned long) fetch type */ +#define __DEFAULT_FETCH_TYPE(t) u##t +#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) +#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG) +#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE) + +#define ASSIGN_FETCH_FUNC(method, type)	\ +	[FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type) + +#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype)	\ +	{.name = _name,				\ +	 .size = _size,					\ +	 .is_signed = sign,				\ +	 .print = PRINT_TYPE_FUNC_NAME(ptype),		\ +	 .fmt = PRINT_TYPE_FMT_NAME(ptype),		\ +	 .fmttype = _fmttype,				\ +	 .fetch = {					\ +ASSIGN_FETCH_FUNC(reg, ftype),				\ +ASSIGN_FETCH_FUNC(stack, ftype),			\ +ASSIGN_FETCH_FUNC(retval, ftype),			\ +ASSIGN_FETCH_FUNC(memory, ftype),			\ +ASSIGN_FETCH_FUNC(symbol, ftype),			\ +ASSIGN_FETCH_FUNC(deref, ftype),			\ +ASSIGN_FETCH_FUNC(bitfield, ftype),			\ +ASSIGN_FETCH_FUNC(file_offset, ftype),			\ +	  }						\ +	} + +#define ASSIGN_FETCH_TYPE(ptype, ftype, sign)			\ +	__ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype) + +#define ASSIGN_FETCH_TYPE_END {} + +#define FETCH_TYPE_STRING	0 +#define FETCH_TYPE_STRSIZE	1 + +/* + * Fetch type information table. + * It's declared as a weak symbol due to conditional compilation. + */ +extern __weak const struct fetch_type kprobes_fetch_type_table[]; +extern __weak const struct fetch_type uprobes_fetch_type_table[]; + +#ifdef CONFIG_KPROBE_EVENT +struct symbol_cache; +unsigned long update_symbol_cache(struct symbol_cache *sc); +void free_symbol_cache(struct symbol_cache *sc); +struct symbol_cache *alloc_symbol_cache(const char *sym, long offset); +#else +/* uprobes do not support symbol fetch methods */ +#define fetch_symbol_u8			NULL +#define fetch_symbol_u16		NULL +#define fetch_symbol_u32		NULL +#define fetch_symbol_u64		NULL +#define fetch_symbol_string		NULL +#define fetch_symbol_string_size	NULL + +struct symbol_cache { +}; +static inline unsigned long __used update_symbol_cache(struct symbol_cache *sc) +{ +	return 0; +} + +static inline void __used free_symbol_cache(struct symbol_cache *sc) +{ +} + +static inline struct symbol_cache * __used +alloc_symbol_cache(const char *sym, long offset) +{ +	return NULL; +} +#endif /* CONFIG_KPROBE_EVENT */ +  struct probe_arg {  	struct fetch_param	fetch;  	struct fetch_param	fetch_size; @@ -124,7 +277,32 @@ struct probe_arg {  	const struct fetch_type	*type;	/* Type of this argument */  }; -static inline __kprobes void call_fetch(struct fetch_param *fprm, +struct trace_probe { +	unsigned int			flags;	/* For TP_FLAG_* */ +	struct ftrace_event_class	class; +	struct ftrace_event_call	call; +	struct list_head 		files; +	ssize_t				size;	/* trace entry size */ +	unsigned int			nr_args; +	struct probe_arg		args[]; +}; + +struct event_file_link { +	struct ftrace_event_file	*file; +	struct list_head		list; +}; + +static inline bool trace_probe_is_enabled(struct trace_probe *tp) +{ +	return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE)); +} + +static inline bool trace_probe_is_registered(struct trace_probe *tp) +{ +	return !!(tp->flags & TP_FLAG_REGISTERED); +} + +static nokprobe_inline void call_fetch(struct fetch_param *fprm,  				 struct pt_regs *regs, void *dest)  {  	return fprm->fn(regs, fprm->data, dest); @@ -142,6 +320,18 @@ static inline int is_good_name(const char *name)  	return 1;  } +static inline struct event_file_link * +find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file) +{ +	struct event_file_link *link; + +	list_for_each_entry(link, &tp->files, list) +		if (link->file == file) +			return link; + +	return NULL; +} +  extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,  		   struct probe_arg *parg, bool is_return, bool is_kprobe); @@ -158,3 +348,53 @@ extern ssize_t traceprobe_probes_write(struct file *file,  		int (*createfn)(int, char**));  extern int traceprobe_command(const char *buf, int (*createfn)(int, char**)); + +/* Sum up total data length for dynamic arraies (strings) */ +static nokprobe_inline int +__get_data_size(struct trace_probe *tp, struct pt_regs *regs) +{ +	int i, ret = 0; +	u32 len; + +	for (i = 0; i < tp->nr_args; i++) +		if (unlikely(tp->args[i].fetch_size.fn)) { +			call_fetch(&tp->args[i].fetch_size, regs, &len); +			ret += len; +		} + +	return ret; +} + +/* Store the value of each argument */ +static nokprobe_inline void +store_trace_args(int ent_size, struct trace_probe *tp, struct pt_regs *regs, +		 u8 *data, int maxlen) +{ +	int i; +	u32 end = tp->size; +	u32 *dl;	/* Data (relative) location */ + +	for (i = 0; i < tp->nr_args; i++) { +		if (unlikely(tp->args[i].fetch_size.fn)) { +			/* +			 * First, we set the relative location and +			 * maximum data length to *dl +			 */ +			dl = (u32 *)(data + tp->args[i].offset); +			*dl = make_data_rloc(maxlen, end - tp->args[i].offset); +			/* Then try to fetch string or dynamic array data */ +			call_fetch(&tp->args[i].fetch, regs, dl); +			/* Reduce maximum length */ +			end += get_rloc_len(*dl); +			maxlen -= get_rloc_len(*dl); +			/* Trick here, convert data_rloc to data_loc */ +			*dl = convert_rloc_to_loc(*dl, +				 ent_size + tp->args[i].offset); +		} else +			/* Just fetching data normally */ +			call_fetch(&tp->args[i].fetch, regs, +				   data + tp->args[i].offset); +	} +} + +extern int set_print_fmt(struct trace_probe *tp, bool is_return);  | 
