mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	tracing/kprobes: Add bitfield type
Add bitfield type for tracing arguments on kprobe-tracer. The syntax of a bitfield type is: b<bit-size>@<bit-offset>/<container-size> e.g. Accessing 2 bits-width field with 4 bits-offset in 32 bits-width data at 4 bytes offseted from the address pointed by AX register: +4(%ax):b2@4/32 Since the width of container data depends on the arch, so I just added the container-size at the end. Cc: 2nddept-manager@sdl.hitachi.co.jp Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com> Cc: Steven Rostedt <rostedt@goodmis.org> LKML-Reference: <20110204125205.9507.11363.stgit@ltc236.sdl.hitachi.co.jp> Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
		
							parent
							
								
									e374536998
								
							
						
					
					
						commit
						1ff511e35e
					
				
					 2 changed files with 118 additions and 2 deletions
				
			
		| 
						 | 
					@ -42,11 +42,25 @@ Synopsis of kprobe_events
 | 
				
			||||||
  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
 | 
					  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
 | 
				
			||||||
  NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
 | 
					  NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
 | 
				
			||||||
  FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
 | 
					  FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
 | 
				
			||||||
		  (u8/u16/u32/u64/s8/s16/s32/s64) and string are supported.
 | 
							  (u8/u16/u32/u64/s8/s16/s32/s64), "string" and bitfield
 | 
				
			||||||
 | 
							  are supported.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (*) only for return probe.
 | 
					  (*) only for return probe.
 | 
				
			||||||
  (**) this is useful for fetching a field of data structures.
 | 
					  (**) this is useful for fetching a field of data structures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Types
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					Several types are supported for fetch-args. Kprobe tracer will access memory
 | 
				
			||||||
 | 
					by given type. Prefix 's' and 'u' means those types are signed and unsigned
 | 
				
			||||||
 | 
					respectively. Traced arguments are shown in decimal (signed) or hex (unsigned).
 | 
				
			||||||
 | 
					String type is a special type, which fetches a "null-terminated" string from
 | 
				
			||||||
 | 
					kernel space. This means it will fail and store NULL if the string container
 | 
				
			||||||
 | 
					has been paged out.
 | 
				
			||||||
 | 
					Bitfield is another special type, which takes 3 parameters, bit-width, bit-
 | 
				
			||||||
 | 
					offset, and container-size (usually 32). The syntax is;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 b<bit-width>@<bit-offset>/<container-size>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Per-Probe Event Filtering
 | 
					Per-Probe Event Filtering
 | 
				
			||||||
-------------------------
 | 
					-------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,6 +353,43 @@ static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
 | 
				
			||||||
	kfree(data);
 | 
						kfree(data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Bitfield fetch function */
 | 
				
			||||||
 | 
					struct bitfield_fetch_param {
 | 
				
			||||||
 | 
						struct fetch_param orig;
 | 
				
			||||||
 | 
						unsigned char hi_shift;
 | 
				
			||||||
 | 
						unsigned char low_shift;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DEFINE_FETCH_bitfield(type)					\
 | 
				
			||||||
 | 
					static __kprobes void FETCH_FUNC_NAME(bitfield, type)(struct pt_regs *regs,\
 | 
				
			||||||
 | 
										    void *data, void *dest)	\
 | 
				
			||||||
 | 
					{									\
 | 
				
			||||||
 | 
						struct bitfield_fetch_param *bprm = data;			\
 | 
				
			||||||
 | 
						type buf = 0;							\
 | 
				
			||||||
 | 
						call_fetch(&bprm->orig, regs, &buf);				\
 | 
				
			||||||
 | 
						if (buf) {							\
 | 
				
			||||||
 | 
							buf <<= bprm->hi_shift;					\
 | 
				
			||||||
 | 
							buf >>= bprm->low_shift;				\
 | 
				
			||||||
 | 
						}								\
 | 
				
			||||||
 | 
						*(type *)dest = buf;						\
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DEFINE_BASIC_FETCH_FUNCS(bitfield)
 | 
				
			||||||
 | 
					#define fetch_bitfield_string NULL
 | 
				
			||||||
 | 
					#define fetch_bitfield_string_size NULL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __kprobes void
 | 
				
			||||||
 | 
					free_bitfield_fetch_param(struct bitfield_fetch_param *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Don't check the bitfield itself, because this must be the
 | 
				
			||||||
 | 
						 * last fetch function.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
 | 
				
			||||||
 | 
							free_deref_fetch_param(data->orig.data);
 | 
				
			||||||
 | 
						else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
 | 
				
			||||||
 | 
							free_symbol_cache(data->orig.data);
 | 
				
			||||||
 | 
						kfree(data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
/* Default (unsigned long) fetch type */
 | 
					/* Default (unsigned long) fetch type */
 | 
				
			||||||
#define __DEFAULT_FETCH_TYPE(t) u##t
 | 
					#define __DEFAULT_FETCH_TYPE(t) u##t
 | 
				
			||||||
#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
 | 
					#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
 | 
				
			||||||
| 
						 | 
					@ -367,6 +404,7 @@ enum {
 | 
				
			||||||
	FETCH_MTD_memory,
 | 
						FETCH_MTD_memory,
 | 
				
			||||||
	FETCH_MTD_symbol,
 | 
						FETCH_MTD_symbol,
 | 
				
			||||||
	FETCH_MTD_deref,
 | 
						FETCH_MTD_deref,
 | 
				
			||||||
 | 
						FETCH_MTD_bitfield,
 | 
				
			||||||
	FETCH_MTD_END,
 | 
						FETCH_MTD_END,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -387,6 +425,7 @@ ASSIGN_FETCH_FUNC(retval, ftype),			\
 | 
				
			||||||
ASSIGN_FETCH_FUNC(memory, ftype),			\
 | 
					ASSIGN_FETCH_FUNC(memory, ftype),			\
 | 
				
			||||||
ASSIGN_FETCH_FUNC(symbol, ftype),			\
 | 
					ASSIGN_FETCH_FUNC(symbol, ftype),			\
 | 
				
			||||||
ASSIGN_FETCH_FUNC(deref, ftype),			\
 | 
					ASSIGN_FETCH_FUNC(deref, ftype),			\
 | 
				
			||||||
 | 
					ASSIGN_FETCH_FUNC(bitfield, ftype),			\
 | 
				
			||||||
	  }						\
 | 
						  }						\
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -430,9 +469,33 @@ static const struct fetch_type *find_fetch_type(const char *type)
 | 
				
			||||||
	if (!type)
 | 
						if (!type)
 | 
				
			||||||
		type = DEFAULT_FETCH_TYPE_STR;
 | 
							type = DEFAULT_FETCH_TYPE_STR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Special case: bitfield */
 | 
				
			||||||
 | 
						if (*type == 'b') {
 | 
				
			||||||
 | 
							unsigned long bs;
 | 
				
			||||||
 | 
							type = strchr(type, '/');
 | 
				
			||||||
 | 
							if (!type)
 | 
				
			||||||
 | 
								goto fail;
 | 
				
			||||||
 | 
							type++;
 | 
				
			||||||
 | 
							if (strict_strtoul(type, 0, &bs))
 | 
				
			||||||
 | 
								goto fail;
 | 
				
			||||||
 | 
							switch (bs) {
 | 
				
			||||||
 | 
							case 8:
 | 
				
			||||||
 | 
								return find_fetch_type("u8");
 | 
				
			||||||
 | 
							case 16:
 | 
				
			||||||
 | 
								return find_fetch_type("u16");
 | 
				
			||||||
 | 
							case 32:
 | 
				
			||||||
 | 
								return find_fetch_type("u32");
 | 
				
			||||||
 | 
							case 64:
 | 
				
			||||||
 | 
								return find_fetch_type("u64");
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								goto fail;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
 | 
						for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
 | 
				
			||||||
		if (strcmp(type, fetch_type_table[i].name) == 0)
 | 
							if (strcmp(type, fetch_type_table[i].name) == 0)
 | 
				
			||||||
			return &fetch_type_table[i];
 | 
								return &fetch_type_table[i];
 | 
				
			||||||
 | 
					fail:
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -586,7 +649,9 @@ static struct trace_probe *alloc_trace_probe(const char *group,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void free_probe_arg(struct probe_arg *arg)
 | 
					static void free_probe_arg(struct probe_arg *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
 | 
						if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn))
 | 
				
			||||||
 | 
							free_bitfield_fetch_param(arg->fetch.data);
 | 
				
			||||||
 | 
						else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
 | 
				
			||||||
		free_deref_fetch_param(arg->fetch.data);
 | 
							free_deref_fetch_param(arg->fetch.data);
 | 
				
			||||||
	else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
 | 
						else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
 | 
				
			||||||
		free_symbol_cache(arg->fetch.data);
 | 
							free_symbol_cache(arg->fetch.data);
 | 
				
			||||||
| 
						 | 
					@ -806,6 +871,41 @@ static int __parse_probe_arg(char *arg, const struct fetch_type *t,
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define BYTES_TO_BITS(nb)	((BITS_PER_LONG * (nb)) / sizeof(long))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Bitfield type needs to be parsed into a fetch function */
 | 
				
			||||||
 | 
					static int __parse_bitfield_probe_arg(const char *bf,
 | 
				
			||||||
 | 
									      const struct fetch_type *t,
 | 
				
			||||||
 | 
									      struct fetch_param *f)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bitfield_fetch_param *bprm;
 | 
				
			||||||
 | 
						unsigned long bw, bo;
 | 
				
			||||||
 | 
						char *tail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (*bf != 'b')
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!bprm)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						bprm->orig = *f;
 | 
				
			||||||
 | 
						f->fn = t->fetch[FETCH_MTD_bitfield];
 | 
				
			||||||
 | 
						f->data = (void *)bprm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bw = simple_strtoul(bf + 1, &tail, 0);	/* Use simple one */
 | 
				
			||||||
 | 
						if (bw == 0 || *tail != '@')
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bf = tail + 1;
 | 
				
			||||||
 | 
						bo = simple_strtoul(bf, &tail, 0);
 | 
				
			||||||
 | 
						if (tail == bf || *tail != '/')
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bprm->hi_shift = BYTES_TO_BITS(t->size) - (bw + bo);
 | 
				
			||||||
 | 
						bprm->low_shift = bprm->hi_shift + bo;
 | 
				
			||||||
 | 
						return (BYTES_TO_BITS(t->size) < (bw + bo)) ? -EINVAL : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* String length checking wrapper */
 | 
					/* String length checking wrapper */
 | 
				
			||||||
static int parse_probe_arg(char *arg, struct trace_probe *tp,
 | 
					static int parse_probe_arg(char *arg, struct trace_probe *tp,
 | 
				
			||||||
			   struct probe_arg *parg, int is_return)
 | 
								   struct probe_arg *parg, int is_return)
 | 
				
			||||||
| 
						 | 
					@ -835,6 +935,8 @@ static int parse_probe_arg(char *arg, struct trace_probe *tp,
 | 
				
			||||||
	parg->offset = tp->size;
 | 
						parg->offset = tp->size;
 | 
				
			||||||
	tp->size += parg->type->size;
 | 
						tp->size += parg->type->size;
 | 
				
			||||||
	ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
 | 
						ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
 | 
				
			||||||
 | 
						if (ret >= 0)
 | 
				
			||||||
 | 
							ret = __parse_bitfield_probe_arg(t, parg->type, &parg->fetch);
 | 
				
			||||||
	if (ret >= 0) {
 | 
						if (ret >= 0) {
 | 
				
			||||||
		parg->fetch_size.fn = get_fetch_size_function(parg->type,
 | 
							parg->fetch_size.fn = get_fetch_size_function(parg->type,
 | 
				
			||||||
							      parg->fetch.fn);
 | 
												      parg->fetch.fn);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue