Skip to content

Commit a42e3c4

Browse files
mhiramatrostedt
authored andcommitted
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@devnote2 Signed-off-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 6218bf9 commit a42e3c4

File tree

5 files changed

+51
-15
lines changed

5 files changed

+51
-15
lines changed

kernel/trace/trace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4848,7 +4848,7 @@ static const char readme_msg[] =
48484848
#else
48494849
"\t $stack<index>, $stack, $retval, $comm,\n"
48504850
#endif
4851-
"\t +|-[u]<offset>(<fetcharg>), \\imm-value\n"
4851+
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
48524852
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
48534853
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
48544854
"\t <type>\\[<array-size>\\]\n"

kernel/trace/trace_kprobe.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
10831083
case FETCH_OP_COMM:
10841084
val = (unsigned long)current->comm;
10851085
break;
1086+
case FETCH_OP_DATA:
1087+
val = (unsigned long)code->data;
1088+
break;
10861089
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
10871090
case FETCH_OP_ARG:
10881091
val = regs_get_kernel_argument(regs, code->param);

kernel/trace/trace_probe.c

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,18 @@ static int str_to_immediate(char *str, unsigned long *imm)
327327
return -EINVAL;
328328
}
329329

330+
static int __parse_imm_string(char *str, char **pbuf, int offs)
331+
{
332+
size_t len = strlen(str);
333+
334+
if (str[len - 1] != '"') {
335+
trace_probe_log_err(offs + len, IMMSTR_NO_CLOSE);
336+
return -EINVAL;
337+
}
338+
*pbuf = kstrndup(str, len - 1, GFP_KERNEL);
339+
return 0;
340+
}
341+
330342
/* Recursive argument parser */
331343
static int
332344
parse_probe_arg(char *arg, const struct fetch_type *type,
@@ -441,7 +453,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
441453
ret = parse_probe_arg(arg, t2, &code, end, flags, offs);
442454
if (ret)
443455
break;
444-
if (code->op == FETCH_OP_COMM) {
456+
if (code->op == FETCH_OP_COMM ||
457+
code->op == FETCH_OP_DATA) {
445458
trace_probe_log_err(offs, COMM_CANT_DEREF);
446459
return -EINVAL;
447460
}
@@ -456,11 +469,19 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
456469
}
457470
break;
458471
case '\\': /* Immediate value */
459-
ret = str_to_immediate(arg + 1, &code->immediate);
460-
if (ret)
461-
trace_probe_log_err(offs + 1, BAD_IMM);
462-
else
463-
code->op = FETCH_OP_IMM;
472+
if (arg[1] == '"') { /* Immediate string */
473+
ret = __parse_imm_string(arg + 2, &tmp, offs + 2);
474+
if (ret)
475+
break;
476+
code->op = FETCH_OP_DATA;
477+
code->data = tmp;
478+
} else {
479+
ret = str_to_immediate(arg + 1, &code->immediate);
480+
if (ret)
481+
trace_probe_log_err(offs + 1, BAD_IMM);
482+
else
483+
code->op = FETCH_OP_IMM;
484+
}
464485
break;
465486
}
466487
if (!ret && code->op == FETCH_OP_NOP) {
@@ -560,8 +581,11 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
560581
}
561582
}
562583

563-
/* Since $comm can not be dereferred, we can find $comm by strcmp */
564-
if (strcmp(arg, "$comm") == 0) {
584+
/*
585+
* Since $comm and immediate string can not be dereferred,
586+
* we can find those by strcmp.
587+
*/
588+
if (strcmp(arg, "$comm") == 0 || strncmp(arg, "\\\"", 2) == 0) {
565589
/* The type of $comm must be "string", and not an array. */
566590
if (parg->count || (t && strcmp(t, "string")))
567591
return -EINVAL;
@@ -598,7 +622,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
598622
if (!strcmp(parg->type->name, "string") ||
599623
!strcmp(parg->type->name, "ustring")) {
600624
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
601-
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM) {
625+
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
626+
code->op != FETCH_OP_DATA) {
602627
trace_probe_log_err(offset + (t ? (t - arg) : 0),
603628
BAD_STRING);
604629
ret = -EINVAL;
@@ -607,9 +632,10 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
607632
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM) ||
608633
parg->count) {
609634
/*
610-
* IMM and COMM is pointing actual address, those must
611-
* be kept, and if parg->count != 0, this is an array
612-
* of string pointers instead of string address itself.
635+
* IMM, DATA and COMM is pointing actual address, those
636+
* must be kept, and if parg->count != 0, this is an
637+
* array of string pointers instead of string address
638+
* itself.
613639
*/
614640
code++;
615641
if (code->op != FETCH_OP_NOP) {
@@ -683,7 +709,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
683709
fail:
684710
if (ret) {
685711
for (code = tmp; code < tmp + FETCH_INSN_MAX; code++)
686-
if (code->op == FETCH_NOP_SYMBOL)
712+
if (code->op == FETCH_NOP_SYMBOL ||
713+
code->op == FETCH_OP_DATA)
687714
kfree(code->data);
688715
}
689716
kfree(tmp);
@@ -754,7 +781,8 @@ void traceprobe_free_probe_arg(struct probe_arg *arg)
754781
struct fetch_insn *code = arg->code;
755782

756783
while (code && code->op != FETCH_OP_END) {
757-
if (code->op == FETCH_NOP_SYMBOL)
784+
if (code->op == FETCH_NOP_SYMBOL ||
785+
code->op == FETCH_OP_DATA)
758786
kfree(code->data);
759787
code++;
760788
}

kernel/trace/trace_probe.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ enum fetch_op {
8989
FETCH_OP_COMM, /* Current comm */
9090
FETCH_OP_ARG, /* Function argument : .param */
9191
FETCH_OP_FOFFS, /* File offset: .immediate */
92+
FETCH_OP_DATA, /* Allocated data: .data */
9293
// Stage 2 (dereference) op
9394
FETCH_OP_DEREF, /* Dereference: .offset */
9495
FETCH_OP_UDEREF, /* User-space Dereference: .offset */
@@ -409,6 +410,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
409410
C(BAD_REG_NAME, "Invalid register name"), \
410411
C(BAD_MEM_ADDR, "Invalid memory address"), \
411412
C(BAD_IMM, "Invalid immediate value"), \
413+
C(IMMSTR_NO_CLOSE, "String is not closed with '\"'"), \
412414
C(FILE_ON_KPROBE, "File offset is not available with kprobe"), \
413415
C(BAD_FILE_OFFS, "Invalid file offset value"), \
414416
C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \

kernel/trace/trace_uprobe.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
248248
case FETCH_OP_COMM:
249249
val = FETCH_TOKEN_COMM;
250250
break;
251+
case FETCH_OP_DATA:
252+
val = (unsigned long)code->data;
253+
break;
251254
case FETCH_OP_FOFFS:
252255
val = translate_user_vaddr(code->immediate);
253256
break;

0 commit comments

Comments
 (0)