Skip to content

Commit b26a124

Browse files
committed
tracing/probes: Add symstr type for dynamic events
Add 'symstr' type for storing the kernel symbol as a string data instead of the symbol address. This allows us to filter the events by wildcard symbol name. e.g. # echo 'e:wqfunc workqueue.workqueue_execute_start symname=$function:symstr' >> dynamic_events # cat events/eprobes/wqfunc/format name: wqfunc ID: 2110 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:__data_loc char[] symname; offset:8; size:4; signed:1; print fmt: " symname=\"%s\"", __get_str(symname) Note that there is already 'symbol' type which just change the print format (so it still stores the symbol address in the tracing ring buffer.) On the other hand, 'symstr' type stores the actual "symbol+offset/size" data as a string. Link: https://lore.kernel.org/all/166679930847.1528100.4124308529180235965.stgit@devnote3/ Signed-off-by: Masami Hiramatsu (Google) <[email protected]>
1 parent 3b7ddab commit b26a124

File tree

5 files changed

+91
-26
lines changed

5 files changed

+91
-26
lines changed

Documentation/trace/kprobetrace.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ Synopsis of kprobe_events
5858
NAME=FETCHARG : Set NAME as the argument name of FETCHARG.
5959
FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types
6060
(u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
61-
(x8/x16/x32/x64), "string", "ustring" and bitfield
62-
are supported.
61+
(x8/x16/x32/x64), "string", "ustring", "symbol", "symstr"
62+
and bitfield are supported.
6363

6464
(\*1) only for the probe on function entry (offs == 0).
6565
(\*2) only for return probe.
@@ -96,6 +96,10 @@ offset, and container-size (usually 32). The syntax is::
9696

9797
Symbol type('symbol') is an alias of u32 or u64 type (depends on BITS_PER_LONG)
9898
which shows given pointer in "symbol+offset" style.
99+
On the other hand, symbol-string type ('symstr') converts the given address to
100+
"symbol+offset/symbolsize" style and stores it as a null-terminated string.
101+
With 'symstr' type, you can filter the event with wildcard pattern of the
102+
symbols, and you don't need to solve symbol name by yourself.
99103
For $comm, the default type is "string"; any other type is invalid.
100104

101105
.. _user_mem_access:

kernel/trace/trace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5608,7 +5608,7 @@ static const char readme_msg[] =
56085608
"\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n"
56095609
"\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n"
56105610
"\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n"
5611-
"\t <type>\\[<array-size>\\]\n"
5611+
"\t symstr, <type>\\[<array-size>\\]\n"
56125612
#ifdef CONFIG_HIST_TRIGGERS
56135613
"\t field: <stype> <name>;\n"
56145614
"\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n"

kernel/trace/trace_probe.c

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,11 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
7676
/* Fetch type information table */
7777
static const struct fetch_type probe_fetch_types[] = {
7878
/* Special types */
79-
__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1,
79+
__ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1,
8080
"__data_loc char[]"),
81-
__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1,
81+
__ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1,
82+
"__data_loc char[]"),
83+
__ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1,
8284
"__data_loc char[]"),
8385
/* Basic types */
8486
ASSIGN_FETCH_TYPE(u8, u8, 0),
@@ -662,23 +664,35 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
662664

663665
ret = -EINVAL;
664666
/* Store operation */
665-
if (!strcmp(parg->type->name, "string") ||
666-
!strcmp(parg->type->name, "ustring")) {
667-
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
668-
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
669-
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
670-
trace_probe_log_err(offset + (t ? (t - arg) : 0),
671-
BAD_STRING);
672-
goto fail;
667+
if (parg->type->is_string) {
668+
if (!strcmp(parg->type->name, "symstr")) {
669+
if (code->op != FETCH_OP_REG && code->op != FETCH_OP_STACK &&
670+
code->op != FETCH_OP_RETVAL && code->op != FETCH_OP_ARG &&
671+
code->op != FETCH_OP_DEREF && code->op != FETCH_OP_TP_ARG) {
672+
trace_probe_log_err(offset + (t ? (t - arg) : 0),
673+
BAD_SYMSTRING);
674+
goto fail;
675+
}
676+
} else {
677+
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF &&
678+
code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM &&
679+
code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) {
680+
trace_probe_log_err(offset + (t ? (t - arg) : 0),
681+
BAD_STRING);
682+
goto fail;
683+
}
673684
}
674-
if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
685+
if (!strcmp(parg->type->name, "symstr") ||
686+
(code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM ||
675687
code->op == FETCH_OP_DATA) || code->op == FETCH_OP_TP_ARG ||
676688
parg->count) {
677689
/*
678690
* IMM, DATA and COMM is pointing actual address, those
679691
* must be kept, and if parg->count != 0, this is an
680692
* array of string pointers instead of string address
681693
* itself.
694+
* For the symstr, it doesn't need to dereference, thus
695+
* it just get the value.
682696
*/
683697
code++;
684698
if (code->op != FETCH_OP_NOP) {
@@ -690,6 +704,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
690704
if (!strcmp(parg->type->name, "ustring") ||
691705
code->op == FETCH_OP_UDEREF)
692706
code->op = FETCH_OP_ST_USTRING;
707+
else if (!strcmp(parg->type->name, "symstr"))
708+
code->op = FETCH_OP_ST_SYMSTR;
693709
else
694710
code->op = FETCH_OP_ST_STRING;
695711
code->size = parg->type->size;
@@ -919,17 +935,15 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len,
919935
for (i = 0; i < tp->nr_args; i++) {
920936
parg = tp->args + i;
921937
if (parg->count) {
922-
if ((strcmp(parg->type->name, "string") == 0) ||
923-
(strcmp(parg->type->name, "ustring") == 0))
938+
if (parg->type->is_string)
924939
fmt = ", __get_str(%s[%d])";
925940
else
926941
fmt = ", REC->%s[%d]";
927942
for (j = 0; j < parg->count; j++)
928943
pos += snprintf(buf + pos, LEN_OR_ZERO,
929944
fmt, parg->name, j);
930945
} else {
931-
if ((strcmp(parg->type->name, "string") == 0) ||
932-
(strcmp(parg->type->name, "ustring") == 0))
946+
if (parg->type->is_string)
933947
fmt = ", __get_str(%s)";
934948
else
935949
fmt = ", REC->%s";

kernel/trace/trace_probe.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ enum fetch_op {
9898
FETCH_OP_ST_UMEM, /* Mem: .offset, .size */
9999
FETCH_OP_ST_STRING, /* String: .offset, .size */
100100
FETCH_OP_ST_USTRING, /* User String: .offset, .size */
101+
FETCH_OP_ST_SYMSTR, /* Kernel Symbol String: .offset, .size */
101102
// Stage 4 (modify) op
102103
FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */
103104
// Stage 5 (loop) op
@@ -133,7 +134,8 @@ struct fetch_insn {
133134
struct fetch_type {
134135
const char *name; /* Name of type */
135136
size_t size; /* Byte size of type */
136-
int is_signed; /* Signed flag */
137+
bool is_signed; /* Signed flag */
138+
bool is_string; /* String flag */
137139
print_type_func_t print; /* Print functions */
138140
const char *fmt; /* Format string */
139141
const char *fmttype; /* Name in format file */
@@ -177,16 +179,19 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol);
177179
#define _ADDR_FETCH_TYPE(t) __ADDR_FETCH_TYPE(t)
178180
#define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG)
179181

180-
#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
181-
{.name = _name, \
182+
#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, str, _fmttype) \
183+
{.name = _name, \
182184
.size = _size, \
183-
.is_signed = sign, \
185+
.is_signed = (bool)sign, \
186+
.is_string = (bool)str, \
184187
.print = PRINT_TYPE_FUNC_NAME(ptype), \
185188
.fmt = PRINT_TYPE_FMT_NAME(ptype), \
186189
.fmttype = _fmttype, \
187190
}
191+
192+
/* Non string types can use these macros */
188193
#define _ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \
189-
__ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, #_fmttype)
194+
__ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, 0, #_fmttype)
190195
#define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \
191196
_ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype)
192197

@@ -431,6 +436,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
431436
C(ARRAY_TOO_BIG, "Array number is too big"), \
432437
C(BAD_TYPE, "Unknown type is specified"), \
433438
C(BAD_STRING, "String accepts only memory argument"), \
439+
C(BAD_SYMSTRING, "Symbol String doesn't accept data/userdata"), \
434440
C(BAD_BITFIELD, "Invalid bitfield"), \
435441
C(ARG_NAME_TOO_LONG, "Argument name is too long"), \
436442
C(NO_ARG_NAME, "Argument name is not specified"), \

kernel/trace/trace_probe_tmpl.h

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,37 @@ probe_mem_read(void *dest, void *src, size_t size);
6767
static nokprobe_inline int
6868
probe_mem_read_user(void *dest, void *src, size_t size);
6969

70+
static nokprobe_inline int
71+
fetch_store_symstrlen(unsigned long addr)
72+
{
73+
char namebuf[KSYM_SYMBOL_LEN];
74+
int ret;
75+
76+
ret = sprint_symbol(namebuf, addr);
77+
if (ret < 0)
78+
return 0;
79+
80+
return ret + 1;
81+
}
82+
83+
/*
84+
* Fetch a null-terminated symbol string + offset. Caller MUST set *(u32 *)buf
85+
* with max length and relative data location.
86+
*/
87+
static nokprobe_inline int
88+
fetch_store_symstring(unsigned long addr, void *dest, void *base)
89+
{
90+
int maxlen = get_loc_len(*(u32 *)dest);
91+
void *__dest;
92+
93+
if (unlikely(!maxlen))
94+
return -ENOMEM;
95+
96+
__dest = get_loc_data(dest, base);
97+
98+
return sprint_symbol(__dest, addr);
99+
}
100+
70101
/* From the 2nd stage, routine is same */
71102
static nokprobe_inline int
72103
process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
@@ -99,16 +130,22 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
99130
stage3:
100131
/* 3rd stage: store value to buffer */
101132
if (unlikely(!dest)) {
102-
if (code->op == FETCH_OP_ST_STRING) {
133+
switch (code->op) {
134+
case FETCH_OP_ST_STRING:
103135
ret = fetch_store_strlen(val + code->offset);
104136
code++;
105137
goto array;
106-
} else if (code->op == FETCH_OP_ST_USTRING) {
138+
case FETCH_OP_ST_USTRING:
107139
ret += fetch_store_strlen_user(val + code->offset);
108140
code++;
109141
goto array;
110-
} else
142+
case FETCH_OP_ST_SYMSTR:
143+
ret += fetch_store_symstrlen(val + code->offset);
144+
code++;
145+
goto array;
146+
default:
111147
return -EILSEQ;
148+
}
112149
}
113150

114151
switch (code->op) {
@@ -129,6 +166,10 @@ process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val,
129166
loc = *(u32 *)dest;
130167
ret = fetch_store_string_user(val + code->offset, dest, base);
131168
break;
169+
case FETCH_OP_ST_SYMSTR:
170+
loc = *(u32 *)dest;
171+
ret = fetch_store_symstring(val + code->offset, dest, base);
172+
break;
132173
default:
133174
return -EILSEQ;
134175
}

0 commit comments

Comments
 (0)