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

Showing the changes to kernel (3 of 6 files). Show all

Unified · Split

3 files
  1. kernel/trace/trace.c +2 −2
  2. kernel/trace/trace_probe.c +115 −54
  3. kernel/trace/trace_probe.h +4 −1
kernel/trace/trace.c+2−2
@@ -4322,8 +4322,8 @@ static const char readme_msg[] = · 4321 unmodified lines
4322#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API4322#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
4323 "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"4323 "\t $stack<index>, $stack, $retval, $comm, $arg<N>,\n"
4324#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS4324#ifdef CONFIG_PROBE_EVENTS_BTF_ARGS
4325 "\t [(structname)]<argname>[->field[->field|.field...]],\n"4325 "\t [(structname[,field])]<argname>[->field[->field|.field...]],\n"
4326 "\t [(structname)](fetcharg)->field[->field|.field...],\n"4326 "\t [(structname[,field])](fetcharg)->field[->field|.field...],\n"
4327#endif4327#endif
4328#else4328#else
4329 "\t $stack<index>, $stack, $retval, $comm,\n"4329 "\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
568 return ret;568 return ret;
569}569}
570 570
571/* Inner loop for solving dot operator ('.'). Return bit-offset of the given field */
572static 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}
571/*629/*
572 * Parse the field of data structure. The @type must be a pointer type630 * Parse the field of data structure. The @type must be a pointer type
573 * pointing the target data structure type.631 * 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
577 struct traceprobe_parse_context *ctx)635 struct traceprobe_parse_context *ctx)
578{636{
579 struct fetch_insn *code = *pcode;637 struct fetch_insn *code = *pcode;
580 const struct btf_member *field;
581 u32 bitoffs, anon_offs;
582 bool is_struct = ctx->struct_btf != NULL;
583 struct btf *btf = ctx_btf(ctx);638 struct btf *btf = ctx_btf(ctx);
584 char *next;639 bool is_first_field = true;
585 int is_ptr;640 int bitoffs;
586 641
587 do {642 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)) {
589 /* Outer loop for solving arrow operator ('->') */645 /* Outer loop for solving arrow operator ('->') */
590 if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {646 if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) {
591 trace_probe_log_err(ctx->offset, NO_PTR_STRCT);647 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
599 return -EINVAL;655 return -EINVAL;
600 }656 }
601 }657 }
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);
645 658
659 bitoffs = get_bitoffset_of_field(&fieldname, &type, ctx);
660 if (bitoffs < 0)
661 return bitoffs;
646 if (++code == end) {662 if (++code == end) {
647 trace_probe_log_err(ctx->offset, TOO_MANY_OPS);663 trace_probe_log_err(ctx->offset, TOO_MANY_OPS);
648 return -EINVAL;664 return -EINVAL;
649 }665 }
650 code->op = FETCH_OP_DEREF; /* TODO: user deref support */666 code->op = FETCH_OP_DEREF; /* TODO: user deref support */
651 code->offset = bitoffs / 8;667 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 }
652 *pcode = code;672 *pcode = code;
653 673
654 ctx->last_bitoffs = bitoffs % 8;674 ctx->last_bitoffs = bitoffs % 8;
655 ctx->last_type = type;675 ctx->last_type = type;
676 is_first_field = false;
656 } while (fieldname);677 } while (fieldname);
657 678
658 return 0;679 return 0;
@@ -808,6 +829,46 @@ static int query_btf_struct(const char *sname, struct traceprobe_parse_context * · 149 unmodified lines
808 return 0;829 return 0;
809}830}
810 831
832static 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
811/* Find the matching closing parenthesis for a given opening parenthesis. */872/* Find the matching closing parenthesis for a given opening parenthesis. */
812static char *find_matched_close_paren(char *s)873static char *find_matched_close_paren(char *s)
813{874{
@@ -940,14 +1001,14 @@ static int handle_typecast(char *arg, struct fetch_insn **pcode, · 126 unmodified lines
940 tmp = close + 2; /* Skip ">" after inner variable name */1001 tmp = close + 2; /* Skip ">" after inner variable name */
941 1002
942 /* resolve the typecast struct name */1003 /* resolve the typecast struct name */
943 ret = query_btf_struct(arg + 1, ctx);1004 ctx->offset = orig_offset + 1; /* for the '(' */
944 if (ret < 0) {1005 ret = parse_btf_casttype(arg + 1, ctx);
945 trace_probe_log_err(orig_offset + 1, NO_PTR_STRCT);1006 if (ret < 0)
946 return -EINVAL;1007 return ret;
947 }
948 1008
949 ctx->offset = orig_offset + tmp - arg;1009 ctx->offset = orig_offset + tmp - arg;
950 ret = parse_btf_field(tmp, ctx->last_struct, pcode, end, ctx);1010 ret = parse_btf_field(tmp, ctx->last_struct, pcode, end, ctx);
1011 ctx->prefix_byteoffs = 0;
951 return ret;1012 return ret;
952}1013}
953 1014
kernel/trace/trace_probe.h+4−1
@@ -451,6 +451,7 @@ struct traceprobe_parse_context { · 450 unmodified lines
451 unsigned int flags;451 unsigned int flags;
452 int offset;452 int offset;
453 int nested_level;453 int nested_level;
454 int prefix_byteoffs; /* The byte offset of the prefix field of typecast */
454};455};
455 456
456/* Each typecast consumes nested level. So the max number of typecast is 3. */457/* 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
594 C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \595 C(TYPECAST_NOT_EVENT, "Typecasts are only for eprobe fields"), \
595 C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \596 C(TYPECAST_REQ_FIELD, "Typecast requires a field access"), \
596 C(TOO_MANY_NESTED, "Too many nested typecasts/dereferences"), \597 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"),
598 601
599#undef C602#undef C
600#define C(a, b) TP_ERR_##a603#define C(a, b) TP_ERR_##a

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t