Skip to content

Commit c440adf

Browse files
committed
tracing/probes: Support BTF based data structure field access
Using BTF to access the fields of a data structure. You can use this for accessing the field with '->' or '.' operation with BTF argument. # echo 't sched_switch next=next->pid vruntime=next->se.vruntime' \ > dynamic_events # echo 1 > events/tracepoints/sched_switch/enable # head -n 40 trace | tail <idle>-0 [000] d..3. 272.565382: sched_switch: (__probestub_sched_switch+0x4/0x10) next=26 vruntime=956533179 kcompactd0-26 [000] d..3. 272.565406: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 <idle>-0 [000] d..3. 273.069441: sched_switch: (__probestub_sched_switch+0x4/0x10) next=9 vruntime=956533179 kworker/0:1-9 [000] d..3. 273.069464: sched_switch: (__probestub_sched_switch+0x4/0x10) next=26 vruntime=956579181 kcompactd0-26 [000] d..3. 273.069480: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 <idle>-0 [000] d..3. 273.141434: sched_switch: (__probestub_sched_switch+0x4/0x10) next=22 vruntime=956533179 kworker/u2:1-22 [000] d..3. 273.141461: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 <idle>-0 [000] d..3. 273.480872: sched_switch: (__probestub_sched_switch+0x4/0x10) next=22 vruntime=956585857 kworker/u2:1-22 [000] d..3. 273.480905: sched_switch: (__probestub_sched_switch+0x4/0x10) next=70 vruntime=959533179 sh-70 [000] d..3. 273.481102: sched_switch: (__probestub_sched_switch+0x4/0x10) next=0 vruntime=0 Link: https://lore.kernel.org/all/169272157251.160970.9318175874130965571.stgit@devnote2/ Signed-off-by: Masami Hiramatsu (Google) <[email protected]> Reviewed-by: Alan Maguire <[email protected]> Acked-by: Steven Rostedt (Google) <[email protected]>
1 parent 302db0f commit c440adf

File tree

3 files changed

+216
-28
lines changed

3 files changed

+216
-28
lines changed

kernel/trace/trace.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5717,7 +5717,8 @@ static const char readme_msg[] =
57175717
"\t fetcharg: (%<register>|$<efield>), @<address>, @<symbol>[+|-<offset>],\n"
57185718
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
57195719
#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
5720-
"\t $stack<index>, $stack, $retval, $comm, $arg<N>, <argname>\n"
5720+
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
5721+
"\t <argname>[->field[->field|.field...]],\n"
57215722
#else
57225723
"\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
57235724
#endif

kernel/trace/trace_probe.c

Lines changed: 204 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,14 @@ static u32 btf_type_int(const struct btf_type *t)
310310
return *(u32 *)(t + 1);
311311
}
312312

313-
static const char *type_from_btf_id(struct btf *btf, s32 id)
313+
static const char *fetch_type_from_btf_type(struct btf *btf,
314+
const struct btf_type *type,
315+
struct traceprobe_parse_context *ctx)
314316
{
315-
const struct btf_type *t;
316317
u32 intdata;
317-
s32 tid;
318318

319319
/* TODO: const char * could be converted as a string */
320-
t = btf_type_skip_modifiers(btf, id, &tid);
321-
322-
switch (BTF_INFO_KIND(t->info)) {
320+
switch (BTF_INFO_KIND(type->info)) {
323321
case BTF_KIND_ENUM:
324322
/* enum is "int", so convert to "s32" */
325323
return "s32";
@@ -332,7 +330,7 @@ static const char *type_from_btf_id(struct btf *btf, s32 id)
332330
else
333331
return "x32";
334332
case BTF_KIND_INT:
335-
intdata = btf_type_int(t);
333+
intdata = btf_type_int(type);
336334
if (BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) {
337335
switch (BTF_INT_BITS(intdata)) {
338336
case 8:
@@ -355,6 +353,10 @@ static const char *type_from_btf_id(struct btf *btf, s32 id)
355353
case 64:
356354
return "u64";
357355
}
356+
/* bitfield, size is encoded in the type */
357+
ctx->last_bitsize = BTF_INT_BITS(intdata);
358+
ctx->last_bitoffs += BTF_INT_OFFSET(intdata);
359+
return "u64";
358360
}
359361
}
360362
/* TODO: support other types */
@@ -406,15 +408,136 @@ static void clear_btf_context(struct traceprobe_parse_context *ctx)
406408
}
407409
}
408410

409-
static int parse_btf_arg(const char *varname, struct fetch_insn *code,
411+
/* Return 1 if the field separater is arrow operator ('->') */
412+
static int split_next_field(char *varname, char **next_field,
413+
struct traceprobe_parse_context *ctx)
414+
{
415+
char *field;
416+
int ret = 0;
417+
418+
field = strpbrk(varname, ".-");
419+
if (field) {
420+
if (field[0] == '-' && field[1] == '>') {
421+
field[0] = '\0';
422+
field += 2;
423+
ret = 1;
424+
} else if (field[0] == '.') {
425+
field[0] = '\0';
426+
field += 1;
427+
} else {
428+
trace_probe_log_err(ctx->offset + field - varname, BAD_HYPHEN);
429+
return -EINVAL;
430+
}
431+
*next_field = field;
432+
}
433+
434+
return ret;
435+
}
436+
437+
/*
438+
* Parse the field of data structure. The @type must be a pointer type
439+
* pointing the target data structure type.
440+
*/
441+
static int parse_btf_field(char *fieldname, const struct btf_type *type,
442+
struct fetch_insn **pcode, struct fetch_insn *end,
443+
struct traceprobe_parse_context *ctx)
444+
{
445+
struct fetch_insn *code = *pcode;
446+
const struct btf_member *field;
447+
u32 bitoffs, anon_offs;
448+
char *next;
449+
int is_ptr;
450+
s32 tid;
451+
452+
do {
453+
/* Outer loop for solving arrow operator ('->') */
454+
if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
455+
trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
456+
return -EINVAL;
457+
}
458+
/* Convert a struct pointer type to a struct type */
459+
type = btf_type_skip_modifiers(ctx->btf, type->type, &tid);
460+
if (!type) {
461+
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
462+
return -EINVAL;
463+
}
464+
465+
bitoffs = 0;
466+
do {
467+
/* Inner loop for solving dot operator ('.') */
468+
next = NULL;
469+
is_ptr = split_next_field(fieldname, &next, ctx);
470+
if (is_ptr < 0)
471+
return is_ptr;
472+
473+
anon_offs = 0;
474+
field = btf_find_struct_member(ctx->btf, type, fieldname,
475+
&anon_offs);
476+
if (!field) {
477+
trace_probe_log_err(ctx->offset, NO_BTF_FIELD);
478+
return -ENOENT;
479+
}
480+
/* Add anonymous structure/union offset */
481+
bitoffs += anon_offs;
482+
483+
/* Accumulate the bit-offsets of the dot-connected fields */
484+
if (btf_type_kflag(type)) {
485+
bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
486+
ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
487+
} else {
488+
bitoffs += field->offset;
489+
ctx->last_bitsize = 0;
490+
}
491+
492+
type = btf_type_skip_modifiers(ctx->btf, field->type, &tid);
493+
if (!type) {
494+
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
495+
return -EINVAL;
496+
}
497+
498+
ctx->offset += next - fieldname;
499+
fieldname = next;
500+
} while (!is_ptr && fieldname);
501+
502+
if (++code == end) {
503+
trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
504+
return -EINVAL;
505+
}
506+
code->op = FETCH_OP_DEREF; /* TODO: user deref support */
507+
code->offset = bitoffs / 8;
508+
*pcode = code;
509+
510+
ctx->last_bitoffs = bitoffs % 8;
511+
ctx->last_type = type;
512+
} while (fieldname);
513+
514+
return 0;
515+
}
516+
517+
static int parse_btf_arg(char *varname,
518+
struct fetch_insn **pcode, struct fetch_insn *end,
410519
struct traceprobe_parse_context *ctx)
411520
{
521+
struct fetch_insn *code = *pcode;
412522
const struct btf_param *params;
413-
int i;
523+
const struct btf_type *type;
524+
char *field = NULL;
525+
int i, is_ptr;
526+
u32 tid;
414527

415528
if (WARN_ON_ONCE(!ctx->funcname))
416529
return -EINVAL;
417530

531+
is_ptr = split_next_field(varname, &field, ctx);
532+
if (is_ptr < 0)
533+
return is_ptr;
534+
if (!is_ptr && field) {
535+
/* dot-connected field on an argument is not supported. */
536+
trace_probe_log_err(ctx->offset + field - varname,
537+
NOSUP_DAT_ARG);
538+
return -EOPNOTSUPP;
539+
}
540+
418541
if (!ctx->params) {
419542
params = find_btf_func_param(ctx->funcname,
420543
&ctx->nr_params, &ctx->btf,
@@ -436,24 +559,39 @@ static int parse_btf_arg(const char *varname, struct fetch_insn *code,
436559
code->param = i + 1;
437560
else
438561
code->param = i;
439-
return 0;
562+
563+
tid = params[i].type;
564+
goto found;
440565
}
441566
}
442567
trace_probe_log_err(ctx->offset, NO_BTFARG);
443568
return -ENOENT;
569+
570+
found:
571+
type = btf_type_skip_modifiers(ctx->btf, tid, &tid);
572+
if (!type) {
573+
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
574+
return -EINVAL;
575+
}
576+
/* Initialize the last type information */
577+
ctx->last_type = type;
578+
ctx->last_bitoffs = 0;
579+
ctx->last_bitsize = 0;
580+
if (field) {
581+
ctx->offset += field - varname;
582+
return parse_btf_field(field, type, pcode, end, ctx);
583+
}
584+
return 0;
444585
}
445586

446-
static const struct fetch_type *parse_btf_arg_type(int arg_idx,
587+
static const struct fetch_type *parse_btf_arg_type(
447588
struct traceprobe_parse_context *ctx)
448589
{
449590
struct btf *btf = ctx->btf;
450591
const char *typestr = NULL;
451592

452-
if (btf && ctx->params) {
453-
if (ctx->flags & TPARG_FL_TPOINT)
454-
arg_idx--;
455-
typestr = type_from_btf_id(btf, ctx->params[arg_idx].type);
456-
}
593+
if (btf && ctx->last_type)
594+
typestr = fetch_type_from_btf_type(btf, ctx->last_type, ctx);
457595

458596
return find_fetch_type(typestr, ctx->flags);
459597
}
@@ -462,21 +600,45 @@ static const struct fetch_type *parse_btf_retval_type(
462600
struct traceprobe_parse_context *ctx)
463601
{
464602
const char *typestr = NULL;
465-
const struct btf_type *t;
603+
const struct btf_type *type;
466604
struct btf *btf;
467605

468606
if (ctx->funcname) {
469607
/* Do not use ctx->btf, because it must be used with ctx->param */
470-
t = btf_find_func_proto(ctx->funcname, &btf);
471-
if (t) {
472-
typestr = type_from_btf_id(btf, t->type);
608+
type = btf_find_func_proto(ctx->funcname, &btf);
609+
if (type) {
610+
type = btf_type_skip_modifiers(btf, type->type, NULL);
611+
if (!IS_ERR_OR_NULL(type))
612+
typestr = fetch_type_from_btf_type(btf, type, ctx);
473613
btf_put(btf);
474614
}
475615
}
476616

477617
return find_fetch_type(typestr, ctx->flags);
478618
}
479619

620+
static int parse_btf_bitfield(struct fetch_insn **pcode,
621+
struct traceprobe_parse_context *ctx)
622+
{
623+
struct fetch_insn *code = *pcode;
624+
625+
if ((ctx->last_bitsize % 8 == 0) && ctx->last_bitoffs == 0)
626+
return 0;
627+
628+
code++;
629+
if (code->op != FETCH_OP_NOP) {
630+
trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
631+
return -EINVAL;
632+
}
633+
*pcode = code;
634+
635+
code->op = FETCH_OP_MOD_BF;
636+
code->lshift = 64 - (ctx->last_bitsize + ctx->last_bitoffs);
637+
code->rshift = 64 - ctx->last_bitsize;
638+
code->basesize = 64 / 8;
639+
return 0;
640+
}
641+
480642
static bool is_btf_retval_void(const char *funcname)
481643
{
482644
const struct btf_type *t;
@@ -503,14 +665,22 @@ static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr
503665
return ERR_PTR(-EOPNOTSUPP);
504666
}
505667

506-
static int parse_btf_arg(const char *varname, struct fetch_insn *code,
668+
static int parse_btf_arg(char *varname,
669+
struct fetch_insn **pcode, struct fetch_insn *end,
507670
struct traceprobe_parse_context *ctx)
508671
{
509672
trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
510673
return -EOPNOTSUPP;
511674
}
512675

513-
#define parse_btf_arg_type(idx, ctx) \
676+
static int parse_btf_bitfield(struct fetch_insn **pcode,
677+
struct traceprobe_parse_context *ctx)
678+
{
679+
trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
680+
return -EOPNOTSUPP;
681+
}
682+
683+
#define parse_btf_arg_type(ctx) \
514684
find_fetch_type(NULL, ctx->flags)
515685

516686
#define parse_btf_retval_type(ctx) \
@@ -778,6 +948,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
778948

779949
code->op = deref;
780950
code->offset = offset;
951+
/* Reset the last type if used */
952+
ctx->last_type = NULL;
781953
}
782954
break;
783955
case '\\': /* Immediate value */
@@ -801,7 +973,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
801973
trace_probe_log_err(ctx->offset, NOSUP_BTFARG);
802974
return -EINVAL;
803975
}
804-
ret = parse_btf_arg(arg, code, ctx);
976+
ret = parse_btf_arg(arg, pcode, end, ctx);
805977
break;
806978
}
807979
}
@@ -947,16 +1119,17 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
9471119
goto out;
9481120
code[FETCH_INSN_MAX - 1].op = FETCH_OP_END;
9491121

1122+
ctx->last_type = NULL;
9501123
ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1],
9511124
ctx);
9521125
if (ret)
9531126
goto fail;
9541127

9551128
/* Update storing type if BTF is available */
9561129
if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && !t) {
957-
if (code->op == FETCH_OP_ARG)
958-
parg->type = parse_btf_arg_type(code->param, ctx);
959-
else if (code->op == FETCH_OP_RETVAL)
1130+
if (ctx->last_type)
1131+
parg->type = parse_btf_arg_type(ctx);
1132+
else if (ctx->flags & TPARG_FL_RETURN)
9601133
parg->type = parse_btf_retval_type(ctx);
9611134
}
9621135

@@ -1031,6 +1204,11 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
10311204
trace_probe_log_err(ctx->offset + t - arg, BAD_BITFIELD);
10321205
goto fail;
10331206
}
1207+
} else if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) &&
1208+
ctx->last_type) {
1209+
ret = parse_btf_bitfield(&code, ctx);
1210+
if (ret)
1211+
goto fail;
10341212
}
10351213
ret = -EINVAL;
10361214
/* Loop(Array) operation */

kernel/trace/trace_probe.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,9 @@ struct traceprobe_parse_context {
388388
const struct btf_param *params; /* Parameter of the function */
389389
s32 nr_params; /* The number of the parameters */
390390
struct btf *btf; /* The BTF to be used */
391+
const struct btf_type *last_type; /* Saved type */
392+
u32 last_bitoffs; /* Saved bitoffs */
393+
u32 last_bitsize; /* Saved bitsize */
391394
unsigned int flags;
392395
int offset;
393396
};
@@ -503,7 +506,13 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
503506
C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\
504507
C(NOFENTRY_ARGS, "$arg* can be used only on function entry"), \
505508
C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \
506-
C(ARGS_2LONG, "$arg* failed because the argument list is too long"),
509+
C(ARGS_2LONG, "$arg* failed because the argument list is too long"), \
510+
C(ARGIDX_2BIG, "$argN index is too big"), \
511+
C(NO_PTR_STRCT, "This is not a pointer to union/structure."), \
512+
C(NOSUP_DAT_ARG, "Non pointer structure/union argument is not supported."),\
513+
C(BAD_HYPHEN, "Failed to parse single hyphen. Forgot '>'?"), \
514+
C(NO_BTF_FIELD, "This field is not found."), \
515+
C(BAD_BTF_TID, "Failed to get BTF type info."),
507516

508517
#undef C
509518
#define C(a, b) TP_ERR_##a

0 commit comments

Comments
 (0)