History

tracing/probes: Support field specifier option for typecast

Add a field specifier option for the typecast. This works like container_of() macro. (STRUCT[,FIELD[.FIELD2...]])VAR This is equivalent to : container_of(VAR, struct STRUCT, FIELD[.FIELD2...]) For example: echo "f tick_nohz_handler next_tick=(tick_sched,sched_timer)timer->next_tick" >> dynamic_events This will trace tick_nohz_handler() with its tick_sched::next_tick which is converted from @timer by contianer_of(tick, struct tick_sched, sched_timer). So, if you enabkle both fprobes:tick_nohz_handler__entry and timer:hrtimer_expire_entry events, we will see something like: <idle>-0 [002] d.h1. 3778.087272: hrtimer_expire_entry: hrtimer=00000000d63db328 f unction=tick_nohz_handler now=3777450051040 <idle>-0 [002] d.h1. 3778.087281: tick_nohz_handler__entry: (tick_nohz_handler+0x4 /0x140) next_tick=3777450000000 Link: https://lore.kernel.org/all/178271365745.1176915.725923927180862257.stgit@devnote2/

Masami Hiramatsu (Google) committed 4 months ago

Commit f2183451 parent 52c76ec6 files · +135 −64Browse files

Unified · Split

6 files
  1. Documentation/trace/eprobetrace.rst +4 −1
  2. Documentation/trace/fprobetrace.rst +5 −3
  3. Documentation/trace/kprobetrace.rst +5 −3
  4. kernel/trace/trace.c +2 −2
  5. kernel/trace/trace_probe.c +115 −54
  6. kernel/trace/trace_probe.h +4 −1
Documentation/trace/eprobetrace.rst+4−1
@@ -49,7 +49,10 @@ Synopsis of eprobe_events · 48 unmodified lines
4949 (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
5050 a pointer to STRUCT and then derference the pointer defined by
5151 ->MEMBER. Note that when this is used, the FIELD name does not
52− need to be prefixed with a '$'.
52+ need to be prefixed with a '$'. ASGN can be specified optionally.
53+ If ASGN is specified, FIELD will be cast to the same offset
54+ position as the ASGN member, rather than to the beginning of
55+ the STRUCT.
5356 (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
5457 also be used with another FETCHARG instead of FIELD.
5558
Documentation/trace/fprobetrace.rst+5−3
@@ -57,10 +57,12 @@ Synopsis of fprobe-events · 56 unmodified lines
5757 (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types
5858 (x8/x16/x32/x64), "char", "string", "ustring", "symbol", "symstr"
5959 and bitfield are supported.
60− (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
60+ (STRUCT[,ASGN])FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
6161 a pointer to STRUCT and then derference the pointer defined by
62− ->MEMBER.
63− (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
62+ ->MEMBER. ASGN can be specified optionally. If ASGN is specified,
63+ FIELD will be cast to the same offset position as the ASGN member,
64+ rather than to the beginning of the STRUCT.
65+ (STRUCT[,ASGN])(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
6466 also be used with another FETCHARG instead of FIELD.
6567
6668 (\*1) This is available only when BTF is enabled.
Documentation/trace/kprobetrace.rst+5−3
@@ -61,11 +61,13 @@ Synopsis of kprobe_events · 60 unmodified lines
6161 (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char",
6262 "string", "ustring", "symbol", "symstr" and bitfield are
6363 supported.
64− (STRUCT)FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
64+ (STRUCT[,ASGN])FIELD->MEMBER[->MEMBER] : If BTF is supported, typecast FIELD to
6565 a pointer to STRUCT and then derference the pointer defined by
6666 ->MEMBER. Note that this is available only when the probe is
67− on function entry.
68− (STRUCT)(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
67+ on function entry. ASGN can be specified optionally. If ASGN
68+ is specified, FIELD will be cast to the same offset position
69+ as the ASGN member, rather than to the beginning of the STRUCT.
70+ (STRUCT[,ASGN])(FETCHARG)->MEMBER[->MEMBER] : typecast can nest, so the above can
6971 also be used with another FETCHARG instead of FIELD.
7072
7173 (\*1) only for the probe on function entry (offs == 0). Note, this argument access
kernel/trace/trace.c+2−2
@@ -4322,8 +4322,8 @@ static const char readme_msg[] = · 4321 unmodified lines
43224322 #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
43234323 "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
43244324 #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
4325− "\t [(structname)]<argname>[->field[->field|.field...]],\n"
4326− "\t [(structname)](fetcharg)->field[->field|.field...],\n"
4325+ "\t [(structname[,field])]<argname>[->field[->field|.field...]],\n"
4326+ "\t [(structname[,field])](fetcharg)->field[->field|.field...],\n"
43274327 #endif
43284328 #else
43294329 "\t $stack<index>, $stack, $retval, $comm,\n"
kernel/trace/trace_probe.c+115−54
@@ -568,6 +568,64 @@ static int split_next_field(char *varname, char **next_field, · 567 unmodified lines
568568 return ret;
569569 }
570570
571+/* Inner loop for solving dot operator ('.'). Return bit-offset of the given field */
572+static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **ptype,
573+ struct traceprobe_parse_context *ctx)
574+{
575+ const struct btf_type *type = *ptype;
576+ const struct btf_member *field;
577+ struct btf *btf = ctx_btf(ctx);
578+ char *fieldname = *pfieldname;
579+ int bitoffs = 0;
580+ u32 anon_offs;
581+ char *next;
582+ int is_ptr;
583+
584+ do {
585+ next = NULL;
586+ is_ptr = split_next_field(fieldname, &next, ctx);
587+ if (is_ptr < 0)
588+ return is_ptr;
589+
590+ anon_offs = 0;
591+ field = btf_find_struct_member(btf, type, fieldname,
592+ &anon_offs);
593+ if (IS_ERR(field)) {
594+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
595+ return PTR_ERR(field);
596+ }
597+ if (!field) {
598+ trace_probe_log_err(ctx->offset, NO_BTF_FIELD);
599+ return -ENOENT;
600+ }
601+ /* Add anonymous structure/union offset */
602+ bitoffs += anon_offs;
603+
604+ /* Accumulate the bit-offsets of the dot-connected fields */
605+ if (btf_type_kflag(type)) {
606+ bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
607+ ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
608+ } else {
609+ bitoffs += field->offset;
610+ ctx->last_bitsize = 0;
611+ }
612+
613+ type = btf_type_skip_modifiers(btf, field->type, NULL);
614+ if (!type) {
615+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
616+ return -EINVAL;
617+ }
618+
619+ if (next)
620+ ctx->offset += next - fieldname;
621+ fieldname = next;
622+ } while (!is_ptr && fieldname);
623+
624+ *pfieldname = fieldname;
625+ *ptype = type;
626+
627+ return bitoffs;
628+}
571629 /*
572630 * Parse the field of data structure. The @type must be a pointer type
573631 * pointing the target data structure type.
@@ -577,15 +635,13 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, · 3 unmodified lines
577635 struct traceprobe_parse_context *ctx)
578636 {
579637 struct fetch_insn *code = *pcode;
580− const struct btf_member *field;
581− u32 bitoffs, anon_offs;
582− bool is_struct = ctx->struct_btf != NULL;
583638 struct btf *btf = ctx_btf(ctx);
584− char *next;
585− int is_ptr;
639+ bool is_first_field = true;
640+ int bitoffs;
586641
587642 do {
588− if (!is_struct) {
643+ /* For the first field of typecast, @type will be the target structure type. */
644+ if (!(is_first_field && ctx->struct_btf)) {
589645 /* Outer loop for solving arrow operator ('->') */
590646 if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
591647 trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
@@ -599,60 +655,25 @@ static int parse_btf_field(char *fieldname, const struct btf_type *type, · 7 unmodified lines
599655 return -EINVAL;
600656 }
601657 }
602− /* Only the first type can skip being a pointer */
603− is_struct = false;
604−
605− bitoffs = 0;
606− do {
607− /* Inner loop for solving dot operator ('.') */
608− next = NULL;
609− is_ptr = split_next_field(fieldname, &next, ctx);
610− if (is_ptr < 0)
611− return is_ptr;
612−
613− anon_offs = 0;
614− field = btf_find_struct_member(btf, type, fieldname,
615− &anon_offs);
616− if (IS_ERR(field)) {
617− trace_probe_log_err(ctx->offset, BAD_BTF_TID);
618− return PTR_ERR(field);
619− }
620− if (!field) {
621− trace_probe_log_err(ctx->offset, NO_BTF_FIELD);
622− return -ENOENT;
623− }
624− /* Add anonymous structure/union offset */
625− bitoffs += anon_offs;
626−
627− /* Accumulate the bit-offsets of the dot-connected fields */
628− if (btf_type_kflag(type)) {
629− bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
630− ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
631− } else {
632− bitoffs += field->offset;
633− ctx->last_bitsize = 0;
634− }
635−
636− type = btf_type_skip_modifiers(btf, field->type, NULL);
637− if (!type) {
638− trace_probe_log_err(ctx->offset, BAD_BTF_TID);
639− return -EINVAL;
640− }
641−
642− ctx->offset += next - fieldname;
643− fieldname = next;
644− } while (!is_ptr && fieldname);
645658
659+ bitoffs = get_bitoffset_of_field(&fieldname, &type, ctx);
660+ if (bitoffs < 0)
661+ return bitoffs;
646662 if (++code == end) {
647663 trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
648664 return -EINVAL;
649665 }
650666 code->op = FETCH_OP_DEREF; /* TODO: user deref support */
651667 code->offset = bitoffs / 8;
668+ if (is_first_field && ctx->struct_btf) {
669+ /* The first field can be typecasted with field option. */
670+ code->offset -= ctx->prefix_byteoffs;
671+ }
652672 *pcode = code;
653673
654674 ctx->last_bitoffs = bitoffs % 8;
655675 ctx->last_type = type;
676+ is_first_field = false;
656677 } while (fieldname);
657678
658679 return 0;
@@ -808,6 +829,46 @@ static int query_btf_struct(const char *sname, struct traceprobe_parse_context * · 149 unmodified lines
808829 return 0;
809830 }
810831
832+static int parse_btf_casttype(char *casttype, struct traceprobe_parse_context *ctx)
833+{
834+ char *field;
835+ int ret;
836+
837+ /* Field option - evaluated later. */
838+ field = strchr(casttype, ',');
839+ if (field)
840+ *field++ = '\0';
841+
842+ ret = query_btf_struct(casttype, ctx);
843+ if (ret < 0) {
844+ trace_probe_log_err(ctx->offset, NO_PTR_STRCT);
845+ return -EINVAL;
846+ }
847+
848+ if (field) {
849+ struct btf_type *type = (struct btf_type *)ctx->last_struct;
850+
851+ ctx->offset += field - casttype;
852+ ret = get_bitoffset_of_field(&field, &ctx->last_struct, ctx);
853+ if (ret < 0)
854+ return ret;
855+ if (ret % 8) {
856+ trace_probe_log_err(ctx->offset, TYPECAST_NOT_ALIGNED);
857+ return -EINVAL;
858+ }
859+ if (field != NULL) {
860+ /* this means @field skips an arrow operator ("->"). */
861+ trace_probe_log_err(ctx->offset - 2, TYPECAST_BAD_ARROW);
862+ return -EINVAL;
863+ }
864+ ctx->prefix_byteoffs = ret / 8;
865+ /* Restore the original struct type (overwritten by get_bitoffset_of_field) */
866+ ctx->last_struct = type;
867+ }
868+
869+ return ret;
870+}
871+
811872 /* Find the matching closing parenthesis for a given opening parenthesis. */
812873 static char *find_matched_close_paren(char *s)
813874 {
@@ -940,14 +1001,14 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode, · 126 unmodified lines
9401001 tmp = close + 2; /* Skip ">" after inner variable name */
9411002
9421003 /* resolve the typecast struct name */
943− ret = query_btf_struct(arg + 1, ctx);
944− if (ret < 0) {
945− trace_probe_log_err(orig_offset + 1, NO_PTR_STRCT);
946− return -EINVAL;
947− }
1004+ ctx->offset = orig_offset + 1; /* for the '(' */
1005+ ret = parse_btf_casttype(arg + 1, ctx);
1006+ if (ret < 0)
1007+ return ret;
9481008
9491009 ctx->offset = orig_offset + tmp - arg;
9501010 ret = parse_btf_field(tmp, ctx->last_struct, pcode, end, ctx);
1011+ ctx->prefix_byteoffs = 0;
9511012 return ret;
9521013 }
9531014
kernel/trace/trace_probe.h+4−1
@@ -451,6 +451,7 @@ struct traceprobe_parse_context { · 450 unmodified lines
451451 unsigned int flags;
452452 int offset;
453453 int nested_level;
454+ int prefix_byteoffs; /* The byte offset of the prefix field of typecast */
454455 };
455456
456457 /* Each typecast consumes nested level. So the max number of typecast is 3. */
@@ -594,7 +595,9 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, · 137 unmodified lines
594595 C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \
595596 C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \
596597 C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"), \
597− C(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses")
598+ C(TYPECAST_SYM_OFFSET, "@SYM+/-OFFSET with typecast needs parentheses"), \
599+ C(TYPECAST_NOT_ALIGNED, "Typecast field option is not byte-aligned"), \
600+ C(TYPECAST_BAD_ARROW, "Typecast field option does not support -> operator"),
598601
599602 #undef C
600603 #define C(a, b) TP_ERR_##a