| @@ -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 | |