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/
Showing the changes to kernel (3 of 6 files). Show all
Unified · Split
| @@ -4322,8 +4322,8 @@ static const char readme_msg[] = · 4321 unmodified lines | |||
| 4322 | #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API | 4322 | #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_ARGS | 4324 | #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 | #endif | 4327 | #endif |
| 4328 | #else | 4328 | #else |
| 4329 | "\t $stack<index>, $stack, $retval, $comm,\n" | 4329 | "\t $stack<index>, $stack, $retval, $comm,\n" |
| @@ -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 */ | ||
| 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 | } | ||
| 571 | /* | 629 | /* |
| 572 | * Parse the field of data structure. The @type must be a pointer type | 630 | * 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 | ||
| 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 | |||
| 811 | /* Find the matching closing parenthesis for a given opening parenthesis. */ | 872 | /* Find the matching closing parenthesis for a given opening parenthesis. */ |
| 812 | static char *find_matched_close_paren(char *s) | 873 | static 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 | ||
| @@ -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 C | 602 | #undef C |
| 600 | #define C(a, b) TP_ERR_##a | 603 | #define C(a, b) TP_ERR_##a |