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@devbox

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
This commit is contained in:
Masami Hiramatsu
2018-04-25 21:21:55 +09:00
committed by Steven Rostedt (VMware)
parent 60c2e0cebf
commit 40b53b7718
5 changed files with 180 additions and 39 deletions

View File

@@ -341,9 +341,9 @@ static int __parse_bitfield_probe_arg(const char *bf,
int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
struct probe_arg *parg, bool is_return, bool is_kprobe)
{
struct fetch_insn *code, *tmp = NULL;
const char *t;
int ret;
struct fetch_insn *code, *scode, *tmp = NULL;
char *t, *t2;
int ret, len;
if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n", arg);
@@ -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);
return -ENOMEM;
}
t = strchr(parg->comm, ':');
t = strchr(arg, ':');
if (t) {
arg[t - parg->comm] = '\0';
t++;
*t = '\0';
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
* dereferenced.
*/
if (!t && strcmp(arg, "$comm") == 0)
t = "string";
parg->type = find_fetch_type(t);
parg->type = find_fetch_type("string");
else
parg->type = find_fetch_type(t);
if (!parg->type) {
pr_info("Unsupported type: %s\n", t);
return -EINVAL;
}
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);
if (!code)
@@ -391,10 +409,20 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
ret = -EINVAL;
goto fail;
}
/* Since IMM or COMM must be the 1st insn, this is safe */
if (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM)
if (code->op != FETCH_OP_DEREF || parg->count) {
/*
* 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++;
if (code->op != FETCH_OP_NOP) {
ret = -E2BIG;
goto fail;
}
}
code->op = FETCH_OP_ST_STRING; /* In DEREF case, replace it */
code->size = parg->type->size;
parg->dynamic = true;
} else if (code->op == FETCH_OP_DEREF) {
code->op = FETCH_OP_ST_MEM;
@@ -408,12 +436,29 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
code->op = FETCH_OP_ST_RAW;
code->size = parg->type->size;
}
scode = code;
/* Modify operation */
if (t != NULL) {
ret = __parse_bitfield_probe_arg(t, parg->type, &code);
if (ret)
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->op = FETCH_OP_END;
@@ -452,14 +497,17 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
kfree(arg->code);
kfree(arg->name);
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,
bool is_return)
{
int i;
struct probe_arg *parg;
int i, j;
int pos = 0;
const char *fmt, *arg;
if (!is_return) {
@@ -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;
}
/* 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);
for (i = 0; i < tp->nr_args; i++) {
pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
tp->args[i].name, tp->args[i].type->fmt);
parg = tp->args + i;
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);
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,
", __get_str(%s)",
tp->args[i].name);
else
pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
tp->args[i].name);
fmt, parg->name);
}
}
#undef LEN_OR_ZERO
/* return the length of print_fmt */
return pos;
}
#undef LEN_OR_ZERO
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,
/* Set argument names as fields */
for (i = 0; i < tp->nr_args; i++) {
struct probe_arg *parg = &tp->args[i];
const char *fmt = parg->type->fmttype;
int size = parg->type->size;
ret = trace_define_field(event_call, parg->type->fmttype,
parg->name,
offset + parg->offset,
parg->type->size,
if (parg->fmt)
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,
FILTER_OTHER);
if (ret)