[RFC PATCH 8/8] tracing: probeevent: Add an array for basic types

2018-02-13 Thread Masami Hiramatsu
Add an array for basic types. This allows user to get arraied
basic types from memory address.
The array type syntax is

TYPE[N]

Where TYPE is one of basic type (u8/16/32/64,s8/16/32/64, and
x8/16/32/64) and N is a fixed value.

Signed-off-by: Masami Hiramatsu 
---
 Documentation/trace/kprobetrace.txt |7 ++
 kernel/trace/trace_kprobe.c |   17 +
 kernel/trace/trace_probe.c  |  111 ++-
 kernel/trace/trace_probe.h  |   29 -
 kernel/trace/trace_uprobe.c |   17 +
 5 files changed, 144 insertions(+), 37 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt 
b/Documentation/trace/kprobetrace.txt
index ec34640becbd..17d3e1a97d85 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -65,6 +65,13 @@ 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
 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
 kernel space. This means it will fail and store NULL if the string container
 has been paged out.
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index cab5efe0a66c..45e00de573d3 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -835,8 +835,9 @@ static int
 process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
   bool pre)
 {
+   struct fetch_insn *s3 = NULL;
unsigned long val;
-   int ret;
+   int ret, i = 0;
 
/* 1st stage: get value from context */
switch (code->op) {
@@ -877,6 +878,8 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs 
*regs, void *dest,
code++;
}
 
+stage3:
+   s3 = code;
/* 3rd stage: store value to buffer */
switch (code->op) {
case FETCH_OP_ST_RAW:
@@ -902,6 +905,16 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs 
*regs, void *dest,
code++;
}
 
+   /* the last stage: Loop on array */
+   if (code->op == FETCH_OP_LP_ARRAY) {
+   if (++i < code->param) {
+   code = s3;
+   val += s3->size;
+   dest += s3->size;
+   goto stage3;
+   }
+   }
+
return code->op == FETCH_OP_END ? 0 : -EILSEQ;
 }
 NOKPROBE_SYMBOL(process_fetch_insn)
@@ -1248,7 +1261,7 @@ static int register_kprobe_event(struct trace_kprobe *tk)
call->event.funcs = _funcs;
call->class->define_fields = kprobe_event_define_fields;
}
-   if (set_print_fmt(>tp, trace_kprobe_is_return(tk)) < 0)
+   if (traceprobe_set_print_fmt(>tp, trace_kprobe_is_return(tk)) < 0)
return -ENOMEM;
ret = register_trace_event(>event);
if (!ret) {
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 491a640a1a3e..ae96f98506f0 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -378,8 +378,8 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
struct probe_arg *parg, unsigned int flags)
 {
struct fetch_insn *code, *tmp = NULL;
-   const char *t;
-   int ret;
+   char *t, *t2;
+   int ret, len;
 
if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n",  arg);
@@ -390,24 +390,40 @@ 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, , 0);
+   if (strcmp(t2, "]") || parg->count == 0)
+   return -EINVAL;
+   }
}
/*
 * 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) {
   

[RFC PATCH 8/8] tracing: probeevent: Add an array for basic types

2018-02-13 Thread Masami Hiramatsu
Add an array for basic types. This allows user to get arraied
basic types from memory address.
The array type syntax is

TYPE[N]

Where TYPE is one of basic type (u8/16/32/64,s8/16/32/64, and
x8/16/32/64) and N is a fixed value.

Signed-off-by: Masami Hiramatsu 
---
 Documentation/trace/kprobetrace.txt |7 ++
 kernel/trace/trace_kprobe.c |   17 +
 kernel/trace/trace_probe.c  |  111 ++-
 kernel/trace/trace_probe.h  |   29 -
 kernel/trace/trace_uprobe.c |   17 +
 5 files changed, 144 insertions(+), 37 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt 
b/Documentation/trace/kprobetrace.txt
index ec34640becbd..17d3e1a97d85 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -65,6 +65,13 @@ 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
 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
 kernel space. This means it will fail and store NULL if the string container
 has been paged out.
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index cab5efe0a66c..45e00de573d3 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -835,8 +835,9 @@ static int
 process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
   bool pre)
 {
+   struct fetch_insn *s3 = NULL;
unsigned long val;
-   int ret;
+   int ret, i = 0;
 
/* 1st stage: get value from context */
switch (code->op) {
@@ -877,6 +878,8 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs 
*regs, void *dest,
code++;
}
 
+stage3:
+   s3 = code;
/* 3rd stage: store value to buffer */
switch (code->op) {
case FETCH_OP_ST_RAW:
@@ -902,6 +905,16 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs 
*regs, void *dest,
code++;
}
 
+   /* the last stage: Loop on array */
+   if (code->op == FETCH_OP_LP_ARRAY) {
+   if (++i < code->param) {
+   code = s3;
+   val += s3->size;
+   dest += s3->size;
+   goto stage3;
+   }
+   }
+
return code->op == FETCH_OP_END ? 0 : -EILSEQ;
 }
 NOKPROBE_SYMBOL(process_fetch_insn)
@@ -1248,7 +1261,7 @@ static int register_kprobe_event(struct trace_kprobe *tk)
call->event.funcs = _funcs;
call->class->define_fields = kprobe_event_define_fields;
}
-   if (set_print_fmt(>tp, trace_kprobe_is_return(tk)) < 0)
+   if (traceprobe_set_print_fmt(>tp, trace_kprobe_is_return(tk)) < 0)
return -ENOMEM;
ret = register_trace_event(>event);
if (!ret) {
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 491a640a1a3e..ae96f98506f0 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -378,8 +378,8 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
struct probe_arg *parg, unsigned int flags)
 {
struct fetch_insn *code, *tmp = NULL;
-   const char *t;
-   int ret;
+   char *t, *t2;
+   int ret, len;
 
if (strlen(arg) > MAX_ARGSTR_LEN) {
pr_info("Argument is too long.: %s\n",  arg);
@@ -390,24 +390,40 @@ 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, , 0);
+   if (strcmp(t2, "]") || parg->count == 0)
+   return -EINVAL;
+   }
}
/*
 * 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) {