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

tracing/probe: Add immediate string parameter support

Add immediate string parameter (\"string") support to
probe events. This allows you to specify an immediate
(or dummy) parameter instead of fetching a string from
memory.

This feature looks odd, but imagine that you put a probe
on a code to trace some string data. If the code is
compiled into 2 instructions and 1 instruction has a
string on memory but other has no string since it is
optimized out. In that case, you can not fold those into
one event, even if ftrace supported multiple probes on
one event. With this feature, you can set a dummy string
like foo=\"(optimized)":string instead of something
like foo=+0(+0(%bp)):string.

Link: http://lkml.kernel.org/r/156095691687.28024.13372712423865047991.stgit@devnote2Signed-off-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
parent 6218bf9f
...@@ -4848,7 +4848,7 @@ static const char readme_msg[] = ...@@ -4848,7 +4848,7 @@ static const char readme_msg[] =
#else #else
"\t $stack<index>, $stack, $retval, $comm,\n" "\t $stack<index>, $stack, $retval, $comm,\n"
#endif #endif
"\t +|-[u]<offset>(<fetcharg>), \\imm-value\n" "\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\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>, ustring,\n" "\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
"\t <type>\\[<array-size>\\]\n" "\t <type>\\[<array-size>\\]\n"
......
...@@ -1083,6 +1083,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, ...@@ -1083,6 +1083,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
case FETCH_OP_COMM: case FETCH_OP_COMM:
val = (unsigned long)current->comm; val = (unsigned long)current->comm;
break; break;
case FETCH_OP_DATA:
val = (unsigned long)code->data;
break;
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
case FETCH_OP_ARG: case FETCH_OP_ARG:
val = regs_get_kernel_argument(regs, code->param); val = regs_get_kernel_argument(regs, code->param);
......
...@@ -327,6 +327,18 @@ static int str_to_immediate(char *str, unsigned long *imm) ...@@ -327,6 +327,18 @@ static int str_to_immediate(char *str, unsigned long *imm)
return -EINVAL; return -EINVAL;
} }
static int __parse_imm_string(char *str, char **pbuf, int offs)
{
size_t len = strlen(str);
if (str[len - 1] != '"') {
trace_probe_log_err(offs + len, IMMSTR_NO_CLOSE);
return -EINVAL;
}
*pbuf = kstrndup(str, len - 1, GFP_KERNEL);
return 0;
}
/* Recursive argument parser */ /* Recursive argument parser */
static int static int
parse_probe_arg(char *arg, const struct fetch_type *type, parse_probe_arg(char *arg, const struct fetch_type *type,
...@@ -441,7 +453,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ...@@ -441,7 +453,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
ret = parse_probe_arg(arg, t2, &code, end, flags, offs); ret = parse_probe_arg(arg, t2, &code, end, flags, offs);
if (ret) if (ret)
break; break;
if (code->op == FETCH_OP_COMM) { if (code->op == FETCH_OP_COMM ||
code->op == FETCH_OP_DATA) {
trace_probe_log_err(offs, COMM_CANT_DEREF); trace_probe_log_err(offs, COMM_CANT_DEREF);
return -EINVAL; return -EINVAL;
} }
...@@ -456,11 +469,19 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ...@@ -456,11 +469,19 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
} }
break; break;
case '\\': /* Immediate value */ case '\\': /* Immediate value */
if (arg[1] == '"') { /* Immediate string */
ret = __parse_imm_string(arg + 2, &tmp, offs + 2);
if (ret)
break;
code->op = FETCH_OP_DATA;
code->data = tmp;
} else {
ret = str_to_immediate(arg + 1, &code->immediate); ret = str_to_immediate(arg + 1, &code->immediate);
if (ret) if (ret)
trace_probe_log_err(offs + 1, BAD_IMM); trace_probe_log_err(offs + 1, BAD_IMM);
else else
code->op = FETCH_OP_IMM; code->op = FETCH_OP_IMM;
}
break; break;
} }
if (!ret && code->op == FETCH_OP_NOP) { if (!ret && code->op == FETCH_OP_NOP) {
...@@ -560,8 +581,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -560,8 +581,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
} }
} }
/* Since $comm can not be dereferred, we can find $comm by strcmp */ /*
if (strcmp(arg, "$comm") == 0) { * Since $comm and immediate string can not be dereferred,
* we can find those by strcmp.
*/
if (strcmp(arg, "$comm") == 0 || strncmp(arg, "\\\"", 2) == 0) {
/* The type of $comm must be "string", and not an array. */ /* The type of $comm must be "string", and not an array. */
if (parg->count || (t && strcmp(t, "string"))) if (parg->count || (t && strcmp(t, "string")))
return -EINVAL; return -EINVAL;
...@@ -598,7 +622,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -598,7 +622,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
if (!strcmp(parg->type->name, "string") || if (!strcmp(parg->type->name, "string") ||
!strcmp(parg->type->name, "ustring")) { !strcmp(parg->type->name, "ustring")) {
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF && if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM) { code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
code->op != FETCH_OP_DATA) {
trace_probe_log_err(offset + (t ? (t - arg) : 0), trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING); BAD_STRING);
ret = -EINVAL; ret = -EINVAL;
...@@ -607,9 +632,10 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -607,9 +632,10 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) || if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) ||
parg->count) { parg->count) {
/* /*
* IMM and COMM is pointing actual address, those must * IMM, DATA and COMM is pointing actual address, those
* be kept, and if parg->count != 0, this is an array * must be kept, and if parg->count != 0, this is an
* of string pointers instead of string address itself. * array of string pointers instead of string address
* itself.
*/ */
code++; code++;
if (code->op != FETCH_OP_NOP) { if (code->op != FETCH_OP_NOP) {
...@@ -683,7 +709,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, ...@@ -683,7 +709,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
fail: fail:
if (ret) { if (ret) {
for (code = tmp; code < tmp + FETCH_INSN_MAX; code++) for (code = tmp; code < tmp + FETCH_INSN_MAX; code++)
if (code->op == FETCH_NOP_SYMBOL) if (code->op == FETCH_NOP_SYMBOL ||
code->op == FETCH_OP_DATA)
kfree(code->data); kfree(code->data);
} }
kfree(tmp); kfree(tmp);
...@@ -754,7 +781,8 @@ void traceprobe_free_probe_arg(struct probe_arg *arg) ...@@ -754,7 +781,8 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
struct fetch_insn *code = arg->code; struct fetch_insn *code = arg->code;
while (code && code->op != FETCH_OP_END) { while (code && code->op != FETCH_OP_END) {
if (code->op == FETCH_NOP_SYMBOL) if (code->op == FETCH_NOP_SYMBOL ||
code->op == FETCH_OP_DATA)
kfree(code->data); kfree(code->data);
code++; code++;
} }
......
...@@ -89,6 +89,7 @@ enum fetch_op { ...@@ -89,6 +89,7 @@ enum fetch_op {
FETCH_OP_COMM, /* Current comm */ FETCH_OP_COMM, /* Current comm */
FETCH_OP_ARG, /* Function argument : .param */ FETCH_OP_ARG, /* Function argument : .param */
FETCH_OP_FOFFS, /* File offset: .immediate */ FETCH_OP_FOFFS, /* File offset: .immediate */
FETCH_OP_DATA, /* Allocated data: .data */
// Stage 2 (dereference) op // Stage 2 (dereference) op
FETCH_OP_DEREF, /* Dereference: .offset */ FETCH_OP_DEREF, /* Dereference: .offset */
FETCH_OP_UDEREF, /* User-space Dereference: .offset */ FETCH_OP_UDEREF, /* User-space Dereference: .offset */
...@@ -409,6 +410,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, ...@@ -409,6 +410,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
C(BAD_REG_NAME, "Invalid register name"), \ C(BAD_REG_NAME, "Invalid register name"), \
C(BAD_MEM_ADDR, "Invalid memory address"), \ C(BAD_MEM_ADDR, "Invalid memory address"), \
C(BAD_IMM, "Invalid immediate value"), \ C(BAD_IMM, "Invalid immediate value"), \
C(IMMSTR_NO_CLOSE, "String is not closed with '\"'"), \
C(FILE_ON_KPROBE, "File offset is not available with kprobe"), \ C(FILE_ON_KPROBE, "File offset is not available with kprobe"), \
C(BAD_FILE_OFFS, "Invalid file offset value"), \ C(BAD_FILE_OFFS, "Invalid file offset value"), \
C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \ C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \
......
...@@ -248,6 +248,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest, ...@@ -248,6 +248,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
case FETCH_OP_COMM: case FETCH_OP_COMM:
val = FETCH_TOKEN_COMM; val = FETCH_TOKEN_COMM;
break; break;
case FETCH_OP_DATA:
val = (unsigned long)code->data;
break;
case FETCH_OP_FOFFS: case FETCH_OP_FOFFS:
val = translate_user_vaddr(code->immediate); val = translate_user_vaddr(code->immediate);
break; break;
......
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