| @@ -568,6 +568,64 @@ static int split_next_field(char *varname, char **next_field, · 567 unmodified lines |
| 568 | 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 | 630 | * Parse the field of data structure. The @type must be a pointer type |
| 573 | 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 | 635 | struct traceprobe_parse_context *ctx) |
| 578 | 636 | { |
| 579 | 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 | 638 | struct btf *btf = ctx_btf(ctx); |
| 584 | | − char *next; |
| 585 | | − int is_ptr; |
| 639 | + bool is_first_field = true; |
| 640 | + int bitoffs; |
| 586 | 641 | |
| 587 | 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 | 645 | /* Outer loop for solving arrow operator ('->') */ |
| 590 | 646 | if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { |
| 591 | 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 | 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 | 662 | if (++code == end) { |
| 647 | 663 | trace_probe_log_err(ctx->offset, TOO_MANY_OPS); |
| 648 | 664 | return -EINVAL; |
| 649 | 665 | } |
| 650 | 666 | code->op = FETCH_OP_DEREF; /* TODO: user deref support */ |
| 651 | 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 | 672 | *pcode = code; |
| 653 | 673 | |
| 654 | 674 | ctx->last_bitoffs = bitoffs % 8; |
| 655 | 675 | ctx->last_type = type; |
| 676 | + is_first_field = false; |
| 656 | 677 | } while (fieldname); |
| 657 | 678 | |
| 658 | 679 | return 0; |
| @@ -808,6 +829,46 @@ static int query_btf_struct(const char *sname, struct traceprobe_parse_context * · 149 unmodified lines |
| 808 | 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 | 872 | /* Find the matching closing parenthesis for a given opening parenthesis. */ |
| 812 | 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 | 1001 | tmp = close + 2; /* Skip ">" after inner variable name */ |
| 941 | 1002 | |
| 942 | 1003 | /* 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; |
| 948 | 1008 | |
| 949 | 1009 | ctx->offset = orig_offset + tmp - arg; |
| 950 | 1010 | ret = parse_btf_field(tmp, ctx->last_struct, pcode, end, ctx); |
| 1011 | + ctx->prefix_byteoffs = 0; |
| 951 | 1012 | return ret; |
| 952 | 1013 | } |
| 953 | 1014 | |