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 @@ error:
|
||||||
|
|
||||||
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