Commit 40b53b77 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Steven Rostedt (VMware)

tracing: probeevent: Add array type support

Add array type support for probe events.
This allows user to get arraied types from memory address.
The array type syntax is

	TYPE[N]

Where TYPE is one of types (u8/16/32/64,s8/16/32/64,
x8/16/32/64, symbol, string) and N is a fixed value less
than 64.

The string array type is a bit different from other types. For
other base types, <base-type>[1] is equal to <base-type>
(e.g. +0(%di):x32[1] is same as +0(%di):x32.) But string[1] is not
equal to string. The string type itself represents "char array",
but string array type represents "char * array". So, for example,
+0(%di):string[1] is equal to +0(+0(%di)):string.

Link: http://lkml.kernel.org/r/152465891533.26224.6150658225601339931.stgit@devboxSigned-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 60c2e0ce
...@@ -64,9 +64,20 @@ respectively. 'x' prefix implies it is unsigned. Traced arguments are shown ...@@ -64,9 +64,20 @@ respectively. 'x' prefix implies it is unsigned. Traced arguments are shown
in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32' in decimal ('s' and 'u') or hexadecimal ('x'). Without type casting, 'x32'
or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and or 'x64' is used depends on the architecture (e.g. x86-32 uses x32, and
x86-64 uses x64). x86-64 uses x64).
These value types can be an array. To record array data, you can add '[N]'
(where N is a fixed number, less than 64) to the base type.
E.g. 'x16[4]' means an array of x16 (2bytes hex) with 4 elements.
Note that the array can be applied to memory type fetchargs, you can not
apply it to registers/stack-entries etc. (for example, '$stack1:x8[8]' is
wrong, but '+8($stack):x8[8]' is OK.)
String type is a special type, which fetches a "null-terminated" string from 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 kernel space. This means it will fail and store NULL if the string container
has been paged out. has been paged out.
The string array type is a bit different from other types. For other base
types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same
as +0(%di):x32.) But string[1] is not equal to string. The string type itself
represents "char array", but string array type represents "char * array".
So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string.
Bitfield is another special type, which takes 3 parameters, bit-width, bit- Bitfield is another special type, which takes 3 parameters, bit-width, bit-
offset, and container-size (usually 32). The syntax is:: offset, and container-size (usually 32). The syntax is::
......
...@@ -4627,7 +4627,8 @@ static const char readme_msg[] = ...@@ -4627,7 +4627,8 @@ static const char readme_msg[] =
"\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n" "\t fetcharg: %<register>, @<address>, @<symbol>[+|-<offset>],\n"
"\t $stack<index>, $stack, $retval, $comm\n" "\t $stack<index>, $stack, $retval, $comm\n"
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
"\t b<bit-width>@<bit-offset>/<container-size>\n" "\t b<bit-width>@<bit-offset>/<container-size>,\n"
"\t <type>\\[<array-size>\\]\n"
#endif #endif
" events/\t\t- Directory containing all trace event subsystems:\n" " events/\t\t- Directory containing all trace event subsystems:\n"
" enable\t\t- Write 0/1 to enable/disable tracing of all events\n" " enable\t\t- Write 0/1 to enable/disable tracing of all events\n"
......
...@@ -341,9 +341,9 @@ static int __parse_bitfield_probe_arg(const char *bf, ...@@ -341,9 +341,9 @@ static int __parse_bitfield_probe_arg(const char *bf,
int traceprobe_parse_probe_arg(char *arg, ssize_t *size, int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
struct probe_arg *parg, bool is_return, bool is_kprobe) struct probe_arg *parg, bool is_return, bool is_kprobe)
{ {
struct fetch_insn *code, *tmp = NULL; struct fetch_insn *code, *scode, *tmp = NULL;
const char *t; char *t, *t2;
int ret; int ret, len;
if (strlen(arg) > MAX_ARGSTR_LEN) { if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n", arg); pr_info("Argument is too long.: %s\n", arg);
...@@ -354,24 +354,42 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, ...@@ -354,24 +354,42 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
pr_info("Failed to allocate memory for command '%s'.\n", arg); pr_info("Failed to allocate memory for command '%s'.\n", arg);
return -ENOMEM; return -ENOMEM;
} }
t = strchr(parg->comm, ':'); t = strchr(arg, ':');
if (t) { if (t) {
arg[t - parg->comm] = '\0'; *t = '\0';
t++; t2 = strchr(++t, '[');
if (t2) {
*t2 = '\0';
parg->count = simple_strtoul(t2 + 1, &t2, 0);
if (strcmp(t2, "]") || parg->count == 0)
return -EINVAL;
if (parg->count > MAX_ARRAY_LEN)
return -E2BIG;
}
} }
/* /*
* The default type of $comm should be "string", and it can't be * The default type of $comm should be "string", and it can't be
* dereferenced. * dereferenced.
*/ */
if (!t && strcmp(arg, "$comm") == 0) if (!t && strcmp(arg, "$comm") == 0)
t = "string"; parg->type = find_fetch_type("string");
parg->type = find_fetch_type(t); else
parg->type = find_fetch_type(t);
if (!parg->type) { if (!parg->type) {
pr_info("Unsupported type: %s\n", t); pr_info("Unsupported type: %s\n", t);
return -EINVAL; return -EINVAL;
} }
parg->offset = *size; parg->offset = *size;
*size += parg->type->size; *size += parg->type->size * (parg->count ?: 1);
if (parg->count) {
len = strlen(parg->type->fmttype) + 6;
parg->fmt = kmalloc(len, GFP_KERNEL);
if (!parg->fmt)
return -ENOMEM;
snprintf(parg->fmt, len, "%s[%d]", parg->type->fmttype,
parg->count);
}
code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL); code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL);
if (!code) if (!code)
...@@ -391,10 +409,20 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, ...@@ -391,10 +409,20 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
/* Since IMM or COMM must be the 1st insn, this is safe */ if (code->op != FETCH_OP_DEREF || parg->count) {
if (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) /*
* IMM and COMM is pointing actual address, those must
* be kept, and if parg->count != 0, this is an array
* of string pointers instead of string address itself.
*/
code++; code++;
if (code->op != FETCH_OP_NOP) {
ret = -E2BIG;
goto fail;
}
}
code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */ code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */
code->size = parg->type->size;
parg->dynamic = true; parg->dynamic = true;
} else if (code->op == FETCH_OP_DEREF) { } else if (code->op == FETCH_OP_DEREF) {
code->op = FETCH_OP_ST_MEM; code->op = FETCH_OP_ST_MEM;
...@@ -408,12 +436,29 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size, ...@@ -408,12 +436,29 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
code->op = FETCH_OP_ST_RAW; code->op = FETCH_OP_ST_RAW;
code->size = parg->type->size; code->size = parg->type->size;
} }
scode = code;
/* Modify operation */ /* Modify operation */
if (t != NULL) { if (t != NULL) {
ret = __parse_bitfield_probe_arg(t, parg->type, &code); ret = __parse_bitfield_probe_arg(t, parg->type, &code);
if (ret) if (ret)
goto fail; goto fail;
} }
/* Loop(Array) operation */
if (parg->count) {
if (scode->op != FETCH_OP_ST_MEM &&
scode->op != FETCH_OP_ST_STRING) {
pr_info("array only accepts memory or address\n");
ret = -EINVAL;
goto fail;
}
code++;
if (code->op != FETCH_OP_NOP) {
ret = -E2BIG;
goto fail;
}
code->op = FETCH_OP_LP_ARRAY;
code->param = parg->count;
}
code++; code++;
code->op = FETCH_OP_END; code->op = FETCH_OP_END;
...@@ -452,14 +497,17 @@ void traceprobe_free_probe_arg(struct probe_arg *arg) ...@@ -452,14 +497,17 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
kfree(arg->code); kfree(arg->code);
kfree(arg->name); kfree(arg->name);
kfree(arg->comm); kfree(arg->comm);
kfree(arg->fmt);
} }
/* When len=0, we just calculate the needed length */
#define LEN_OR_ZERO (len ? len - pos : 0)
static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
bool is_return) bool is_return)
{ {
int i; struct probe_arg *parg;
int i, j;
int pos = 0; int pos = 0;
const char *fmt, *arg; const char *fmt, *arg;
if (!is_return) { if (!is_return) {
...@@ -470,33 +518,49 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, ...@@ -470,33 +518,49 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP; arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP;
} }
/* When len=0, we just calculate the needed length */
#define LEN_OR_ZERO (len ? len - pos : 0)
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt); pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);
for (i = 0; i < tp->nr_args; i++) { for (i = 0; i < tp->nr_args; i++) {
pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s", parg = tp->args + i;
tp->args[i].name, tp->args[i].type->fmt); pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=", parg->name);
if (parg->count) {
pos += snprintf(buf + pos, LEN_OR_ZERO, "{%s",
parg->type->fmt);
for (j = 1; j < parg->count; j++)
pos += snprintf(buf + pos, LEN_OR_ZERO, ",%s",
parg->type->fmt);
pos += snprintf(buf + pos, LEN_OR_ZERO, "}");
} else
pos += snprintf(buf + pos, LEN_OR_ZERO, "%s",
parg->type->fmt);
} }
pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg); pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);
for (i = 0; i < tp->nr_args; i++) { for (i = 0; i < tp->nr_args; i++) {
if (strcmp(tp->args[i].type->name, "string") == 0) parg = tp->args + i;
if (parg->count) {
if (strcmp(parg->type->name, "string") == 0)
fmt = ", __get_str(%s[%d])";
else
fmt = ", REC->%s[%d]";
for (j = 0; j < parg->count; j++)
pos += snprintf(buf + pos, LEN_OR_ZERO,
fmt, parg->name, j);
} else {
if (strcmp(parg->type->name, "string") == 0)
fmt = ", __get_str(%s)";
else
fmt = ", REC->%s";
pos += snprintf(buf + pos, LEN_OR_ZERO, pos += snprintf(buf + pos, LEN_OR_ZERO,
", __get_str(%s)", fmt, parg->name);
tp->args[i].name); }
else
pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
tp->args[i].name);
} }
#undef LEN_OR_ZERO
/* return the length of print_fmt */ /* return the length of print_fmt */
return pos; return pos;
} }
#undef LEN_OR_ZERO
int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return) int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return)
{ {
...@@ -524,11 +588,15 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -524,11 +588,15 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
/* Set argument names as fields */ /* Set argument names as fields */
for (i = 0; i < tp->nr_args; i++) { for (i = 0; i < tp->nr_args; i++) {
struct probe_arg *parg = &tp->args[i]; struct probe_arg *parg = &tp->args[i];
const char *fmt = parg->type->fmttype;
ret = trace_define_field(event_call, parg->type->fmttype, int size = parg->type->size;
parg->name,
offset + parg->offset, if (parg->fmt)
parg->type->size, fmt = parg->fmt;
if (parg->count)
size *= parg->count;
ret = trace_define_field(event_call, fmt, parg->name,
offset + parg->offset, size,
parg->type->is_signed, parg->type->is_signed,
FILTER_OTHER); FILTER_OTHER);
if (ret) if (ret)
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#define MAX_TRACE_ARGS 128 #define MAX_TRACE_ARGS 128
#define MAX_ARGSTR_LEN 63 #define MAX_ARGSTR_LEN 63
#define MAX_ARRAY_LEN 64
#define MAX_STRING_SIZE PATH_MAX #define MAX_STRING_SIZE PATH_MAX
/* Reserved field names */ /* Reserved field names */
...@@ -65,6 +66,14 @@ static nokprobe_inline void *get_loc_data(u32 *dl, void *ent) ...@@ -65,6 +66,14 @@ static nokprobe_inline void *get_loc_data(u32 *dl, void *ent)
return (u8 *)ent + get_loc_offs(*dl); return (u8 *)ent + get_loc_offs(*dl);
} }
static nokprobe_inline u32 update_data_loc(u32 loc, int consumed)
{
u32 maxlen = get_loc_len(loc);
u32 offset = get_loc_offs(loc);
return make_data_loc(maxlen - consumed, offset + consumed);
}
/* Printing function type */ /* Printing function type */
typedef int (*print_type_func_t)(struct trace_seq *, void *, void *); typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);
...@@ -86,6 +95,8 @@ enum fetch_op { ...@@ -86,6 +95,8 @@ enum fetch_op {
FETCH_OP_ST_STRING, /* String: .offset, .size */ FETCH_OP_ST_STRING, /* String: .offset, .size */
// Stage 4 (modify) op // Stage 4 (modify) op
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
// Stage 5 (loop) op
FETCH_OP_LP_ARRAY, /* Array: .param = loop count */
FETCH_OP_END, FETCH_OP_END,
}; };
...@@ -175,6 +186,7 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol); ...@@ -175,6 +186,7 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype) _ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, atype)
#define ASSIGN_FETCH_TYPE_END {} #define ASSIGN_FETCH_TYPE_END {}
#define MAX_ARRAY_LEN 64
#ifdef CONFIG_KPROBE_EVENTS #ifdef CONFIG_KPROBE_EVENTS
bool trace_kprobe_on_func_entry(struct trace_event_call *call); bool trace_kprobe_on_func_entry(struct trace_event_call *call);
...@@ -195,8 +207,10 @@ struct probe_arg { ...@@ -195,8 +207,10 @@ struct probe_arg {
struct fetch_insn *code; struct fetch_insn *code;
bool dynamic;/* Dynamic array (string) is used */ bool dynamic;/* Dynamic array (string) is used */
unsigned int offset; /* Offset from argument entry */ unsigned int offset; /* Offset from argument entry */
unsigned int count; /* Array count */
const char *name; /* Name of this argument */ const char *name; /* Name of this argument */
const char *comm; /* Command of this argument */ const char *comm; /* Command of this argument */
char *fmt; /* Format string if needed */
const struct fetch_type *type; /* Type of this argument */ const struct fetch_type *type; /* Type of this argument */
}; };
......
...@@ -67,10 +67,15 @@ static nokprobe_inline int ...@@ -67,10 +67,15 @@ static nokprobe_inline int
process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
void *dest, void *base) void *dest, void *base)
{ {
int ret = 0; struct fetch_insn *s3 = NULL;
int total = 0, ret = 0, i = 0;
u32 loc = 0;
unsigned long lval = val;
stage2:
/* 2nd stage: dereference memory if needed */ /* 2nd stage: dereference memory if needed */
while (code->op == FETCH_OP_DEREF) { while (code->op == FETCH_OP_DEREF) {
lval = val;
ret = probe_mem_read(&val, (void *)val + code->offset, ret = probe_mem_read(&val, (void *)val + code->offset,
sizeof(val)); sizeof(val));
if (ret) if (ret)
...@@ -78,11 +83,15 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -78,11 +83,15 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
code++; code++;
} }
s3 = code;
stage3:
/* 3rd stage: store value to buffer */ /* 3rd stage: store value to buffer */
if (unlikely(!dest)) { if (unlikely(!dest)) {
if (code->op == FETCH_OP_ST_STRING) if (code->op == FETCH_OP_ST_STRING) {
return fetch_store_strlen(val + code->offset); ret += fetch_store_strlen(val + code->offset);
else code++;
goto array;
} else
return -EILSEQ; return -EILSEQ;
} }
...@@ -94,6 +103,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -94,6 +103,7 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
probe_mem_read(dest, (void *)val + code->offset, code->size); probe_mem_read(dest, (void *)val + code->offset, code->size);
break; break;
case FETCH_OP_ST_STRING: case FETCH_OP_ST_STRING:
loc = *(u32 *)dest;
ret = fetch_store_string(val + code->offset, dest, base); ret = fetch_store_string(val + code->offset, dest, base);
break; break;
default: default:
...@@ -107,6 +117,29 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, ...@@ -107,6 +117,29 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
code++; code++;
} }
array:
/* the last stage: Loop on array */
if (code->op == FETCH_OP_LP_ARRAY) {
total += ret;
if (++i < code->param) {
code = s3;
if (s3->op != FETCH_OP_ST_STRING) {
dest += s3->size;
val += s3->size;
goto stage3;
}
code--;
val = lval + sizeof(char *);
if (dest) {
dest += sizeof(u32);
*(u32 *)dest = update_data_loc(loc, ret);
}
goto stage2;
}
code++;
ret = total;
}
return code->op == FETCH_OP_END ? ret : -EILSEQ; return code->op == FETCH_OP_END ? ret : -EILSEQ;
} }
...@@ -158,12 +191,26 @@ static inline int ...@@ -158,12 +191,26 @@ static inline int
print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args, print_probe_args(struct trace_seq *s, struct probe_arg *args, int nr_args,
u8 *data, void *field) u8 *data, void *field)
{ {
int i; void *p;
int i, j;
for (i = 0; i < nr_args; i++) { for (i = 0; i < nr_args; i++) {
trace_seq_printf(s, " %s=", args[i].name); struct probe_arg *a = args + i;
if (!args[i].type->print(s, data + args[i].offset, field))
return -ENOMEM; trace_seq_printf(s, " %s=", a->name);
if (likely(!a->count)) {
if (!a->type->print(s, data + a->offset, field))
return -ENOMEM;
continue;
}
trace_seq_putc(s, '{');
p = data + a->offset;
for (j = 0; j < a->count; j++) {
if (!a->type->print(s, p, field))
return -ENOMEM;
trace_seq_putc(s, j == a->count - 1 ? '}' : ',');
p += a->type->size;
}
} }
return 0; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment