Add 'symstr' type for storing the kernel symbol as a string data instead of the symbol address. This allows us to filter the events by wildcard symbol name. e.g. # echo 'e:wqfunc workqueue.workqueue_execute_start symname=$function:symstr' >> dynamic_events # cat events/eprobes/wqfunc/format name: wqfunc ID: 2110 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:__data_loc char[] symname; offset:8; size:4; signed:1; print fmt: " symname=\"%s\"", __get_str(symname) Note that there is already 'symbol' type which just change the print format (so it still stores the symbol address in the tracing ring buffer.) On the other hand, 'symstr' type stores the actual "symbol+offset/size" data as a string. Link: https://lore.kernel.org/all/166679930847.1528100.4124308529180235965.stgit@devnote3/
Showing the changes to kernel (4 of 5 files). Show all
Unified · Split
| @@ -5608,7 +5608,7 @@ static const char readme_msg[] = · 5607 unmodified lines | ||
| 5608 | 5608 | "\t +|-[u]<offset>(<fetcharg>), \\imm-value, \\\"imm-string\"\n" |
| 5609 | 5609 | "\t type: s8/16/32/64, u8/16/32/64, x8/16/32/64, string, symbol,\n" |
| 5610 | 5610 | "\t b<bit-width>@<bit-offset>/<container-size>, ustring,\n" |
| 5611 | − "\t <type>\\[<array-size>\\]\n" | |
| 5611 | + "\t symstr, <type>\\[<array-size>\\]\n" | |
| 5612 | 5612 | #ifdef CONFIG_HIST_TRIGGERS |
| 5613 | 5613 | "\t field: <stype> <name>;\n" |
| 5614 | 5614 | "\t stype: u8/u16/u32/u64, s8/s16/s32/s64, pid_t,\n" |
| @@ -76,9 +76,11 @@ const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\""; · 75 unmodified lines | ||
| 76 | 76 | /* Fetch type information table */ |
| 77 | 77 | static const struct fetch_type probe_fetch_types[] = { |
| 78 | 78 | /* Special types */ |
| 79 | − __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, | |
| 79 | + __ASSIGN_FETCH_TYPE("string", string, string, sizeof(u32), 1, 1, | |
| 80 | 80 | "__data_loc char[]"), |
| 81 | − __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, | |
| 81 | + __ASSIGN_FETCH_TYPE("ustring", string, string, sizeof(u32), 1, 1, | |
| 82 | + "__data_loc char[]"), | |
| 83 | + __ASSIGN_FETCH_TYPE("symstr", string, string, sizeof(u32), 1, 1, | |
| 82 | 84 | "__data_loc char[]"), |
| 83 | 85 | /* Basic types */ |
| 84 | 86 | ASSIGN_FETCH_TYPE(u8, u8, 0), |
| @@ -662,16 +664,26 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, · 577 unmodified lines | ||
| 662 | 664 | |
| 663 | 665 | ret = -EINVAL; |
| 664 | 666 | /* Store operation */ |
| 665 | − if (!strcmp(parg->type->name, "string") || | |
| 666 | − !strcmp(parg->type->name, "ustring")) { | |
| 667 | − if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF && | |
| 668 | − code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM && | |
| 669 | − code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) { | |
| 670 | − trace_probe_log_err(offset + (t ? (t - arg) : 0), | |
| 671 | − BAD_STRING); | |
| 672 | − goto fail; | |
| 667 | + if (parg->type->is_string) { | |
| 668 | + if (!strcmp(parg->type->name, "symstr")) { | |
| 669 | + if (code->op != FETCH_OP_REG && code->op != FETCH_OP_STACK && | |
| 670 | + code->op != FETCH_OP_RETVAL && code->op != FETCH_OP_ARG && | |
| 671 | + code->op != FETCH_OP_DEREF && code->op != FETCH_OP_TP_ARG) { | |
| 672 | + trace_probe_log_err(offset + (t ? (t - arg) : 0), | |
| 673 | + BAD_SYMSTRING); | |
| 674 | + goto fail; | |
| 675 | + } | |
| 676 | + } else { | |
| 677 | + if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_UDEREF && | |
| 678 | + code->op != FETCH_OP_IMM && code->op != FETCH_OP_COMM && | |
| 679 | + code->op != FETCH_OP_DATA && code->op != FETCH_OP_TP_ARG) { | |
| 680 | + trace_probe_log_err(offset + (t ? (t - arg) : 0), | |
| 681 | + BAD_STRING); | |
| 682 | + goto fail; | |
| 683 | + } | |
| 673 | 684 | } |
| 674 | − if ((code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM || | |
| 685 | + if (!strcmp(parg->type->name, "symstr") || | |
| 686 | + (code->op == FETCH_OP_IMM || code->op == FETCH_OP_COMM || | |
| 675 | 687 | code->op == FETCH_OP_DATA) || code->op == FETCH_OP_TP_ARG || |
| 676 | 688 | parg->count) { |
| 677 | 689 | /* |
| @@ -679,6 +691,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, · 1 unmodified line | ||
| 679 | 691 | * must be kept, and if parg->count != 0, this is an |
| 680 | 692 | * array of string pointers instead of string address |
| 681 | 693 | * itself. |
| 694 | + * For the symstr, it doesn't need to dereference, thus | |
| 695 | + * it just get the value. | |
| 682 | 696 | */ |
| 683 | 697 | code++; |
| 684 | 698 | if (code->op != FETCH_OP_NOP) { |
| @@ -690,6 +704,8 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, · 5 unmodified lines | ||
| 690 | 704 | if (!strcmp(parg->type->name, "ustring") || |
| 691 | 705 | code->op == FETCH_OP_UDEREF) |
| 692 | 706 | code->op = FETCH_OP_ST_USTRING; |
| 707 | + else if (!strcmp(parg->type->name, "symstr")) | |
| 708 | + code->op = FETCH_OP_ST_SYMSTR; | |
| 693 | 709 | else |
| 694 | 710 | code->op = FETCH_OP_ST_STRING; |
| 695 | 711 | code->size = parg->type->size; |
| @@ -919,8 +935,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, · 223 unmodified lines | ||
| 919 | 935 | for (i = 0; i < tp->nr_args; i++) { |
| 920 | 936 | parg = tp->args + i; |
| 921 | 937 | if (parg->count) { |
| 922 | − if ((strcmp(parg->type->name, "string") == 0) || | |
| 923 | − (strcmp(parg->type->name, "ustring") == 0)) | |
| 938 | + if (parg->type->is_string) | |
| 924 | 939 | fmt = ", __get_str(%s[%d])"; |
| 925 | 940 | else |
| 926 | 941 | fmt = ", REC->%s[%d]"; |
| @@ -928,8 +943,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len, · 1 unmodified line | ||
| 928 | 943 | pos += snprintf(buf + pos, LEN_OR_ZERO, |
| 929 | 944 | fmt, parg->name, j); |
| 930 | 945 | } else { |
| 931 | − if ((strcmp(parg->type->name, "string") == 0) || | |
| 932 | − (strcmp(parg->type->name, "ustring") == 0)) | |
| 946 | + if (parg->type->is_string) | |
| 933 | 947 | fmt = ", __get_str(%s)"; |
| 934 | 948 | else |
| 935 | 949 | fmt = ", REC->%s"; |
| @@ -98,6 +98,7 @@ enum fetch_op { · 97 unmodified lines | ||
| 98 | 98 | FETCH_OP_ST_UMEM, /* Mem: .offset, .size */ |
| 99 | 99 | FETCH_OP_ST_STRING, /* String: .offset, .size */ |
| 100 | 100 | FETCH_OP_ST_USTRING, /* User String: .offset, .size */ |
| 101 | + FETCH_OP_ST_SYMSTR, /* Kernel Symbol String: .offset, .size */ | |
| 101 | 102 | // Stage 4 (modify) op |
| 102 | 103 | FETCH_OP_MOD_BF, /* Bitfield: .basesize, .lshift, .rshift */ |
| 103 | 104 | // Stage 5 (loop) op |
| @@ -133,7 +134,8 @@ struct fetch_insn { · 29 unmodified lines | ||
| 133 | 134 | struct fetch_type { |
| 134 | 135 | const char *name; /* Name of type */ |
| 135 | 136 | size_t size; /* Byte size of type */ |
| 136 | − int is_signed; /* Signed flag */ | |
| 137 | + bool is_signed; /* Signed flag */ | |
| 138 | + bool is_string; /* String flag */ | |
| 137 | 139 | print_type_func_t print; /* Print functions */ |
| 138 | 140 | const char *fmt; /* Format string */ |
| 139 | 141 | const char *fmttype; /* Name in format file */ |
| @@ -177,16 +179,19 @@ DECLARE_BASIC_PRINT_TYPE_FUNC(symbol); · 37 unmodified lines | ||
| 177 | 179 | #define _ADDR_FETCH_TYPE(t) __ADDR_FETCH_TYPE(t) |
| 178 | 180 | #define ADDR_FETCH_TYPE _ADDR_FETCH_TYPE(BITS_PER_LONG) |
| 179 | 181 | |
| 180 | −#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ | |
| 181 | − {.name = _name, \ | |
| 182 | +#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, str, _fmttype) \ | |
| 183 | + {.name = _name, \ | |
| 182 | 184 | .size = _size, \ |
| 183 | − .is_signed = sign, \ | |
| 185 | + .is_signed = (bool)sign, \ | |
| 186 | + .is_string = (bool)str, \ | |
| 184 | 187 | .print = PRINT_TYPE_FUNC_NAME(ptype), \ |
| 185 | 188 | .fmt = PRINT_TYPE_FMT_NAME(ptype), \ |
| 186 | 189 | .fmttype = _fmttype, \ |
| 187 | 190 | } |
| 191 | + | |
| 192 | +/* Non string types can use these macros */ | |
| 188 | 193 | #define _ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype) \ |
| 189 | − __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, #_fmttype) | |
| 194 | + __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, 0, #_fmttype) | |
| 190 | 195 | #define ASSIGN_FETCH_TYPE(ptype, ftype, sign) \ |
| 191 | 196 | _ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, ptype) |
| 192 | 197 | |
| @@ -431,6 +436,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, · 238 unmodified lines | ||
| 431 | 436 | C(ARRAY_TOO_BIG, "Array number is too big"), \ |
| 432 | 437 | C(BAD_TYPE, "Unknown type is specified"), \ |
| 433 | 438 | C(BAD_STRING, "String accepts only memory argument"), \ |
| 439 | + C(BAD_SYMSTRING, "Symbol String doesn't accept data/userdata"), \ | |
| 434 | 440 | C(BAD_BITFIELD, "Invalid bitfield"), \ |
| 435 | 441 | C(ARG_NAME_TOO_LONG, "Argument name is too long"), \ |
| 436 | 442 | C(NO_ARG_NAME, "Argument name is not specified"), \ |
| @@ -67,6 +67,37 @@ probe_mem_read(void *dest, void *src, size_t size); · 66 unmodified lines | ||
| 67 | 67 | static nokprobe_inline int |
| 68 | 68 | probe_mem_read_user(void *dest, void *src, size_t size); |
| 69 | 69 | |
| 70 | +static nokprobe_inline int | |
| 71 | +fetch_store_symstrlen(unsigned long addr) | |
| 72 | +{ | |
| 73 | + char namebuf[KSYM_SYMBOL_LEN]; | |
| 74 | + int ret; | |
| 75 | + | |
| 76 | + ret = sprint_symbol(namebuf, addr); | |
| 77 | + if (ret < 0) | |
| 78 | + return 0; | |
| 79 | + | |
| 80 | + return ret + 1; | |
| 81 | +} | |
| 82 | + | |
| 83 | +/* | |
| 84 | + * Fetch a null-terminated symbol string + offset. Caller MUST set *(u32 *)buf | |
| 85 | + * with max length and relative data location. | |
| 86 | + */ | |
| 87 | +static nokprobe_inline int | |
| 88 | +fetch_store_symstring(unsigned long addr, void *dest, void *base) | |
| 89 | +{ | |
| 90 | + int maxlen = get_loc_len(*(u32 *)dest); | |
| 91 | + void *__dest; | |
| 92 | + | |
| 93 | + if (unlikely(!maxlen)) | |
| 94 | + return -ENOMEM; | |
| 95 | + | |
| 96 | + __dest = get_loc_data(dest, base); | |
| 97 | + | |
| 98 | + return sprint_symbol(__dest, addr); | |
| 99 | +} | |
| 100 | + | |
| 70 | 101 | /* From the 2nd stage, routine is same */ |
| 71 | 102 | static nokprobe_inline int |
| 72 | 103 | process_fetch_insn_bottom(struct fetch_insn *code, unsigned long val, |
| @@ -99,16 +130,22 @@ stage2: · 26 unmodified lines | ||
| 99 | 130 | stage3: |
| 100 | 131 | /* 3rd stage: store value to buffer */ |
| 101 | 132 | if (unlikely(!dest)) { |
| 102 | − if (code->op == FETCH_OP_ST_STRING) { | |
| 133 | + switch (code->op) { | |
| 134 | + case FETCH_OP_ST_STRING: | |
| 103 | 135 | ret = fetch_store_strlen(val + code->offset); |
| 104 | 136 | code++; |
| 105 | 137 | goto array; |
| 106 | − } else if (code->op == FETCH_OP_ST_USTRING) { | |
| 138 | + case FETCH_OP_ST_USTRING: | |
| 107 | 139 | ret += fetch_store_strlen_user(val + code->offset); |
| 108 | 140 | code++; |
| 109 | 141 | goto array; |
| 110 | − } else | |
| 142 | + case FETCH_OP_ST_SYMSTR: | |
| 143 | + ret += fetch_store_symstrlen(val + code->offset); | |
| 144 | + code++; | |
| 145 | + goto array; | |
| 146 | + default: | |
| 111 | 147 | return -EILSEQ; |
| 148 | + } | |
| 112 | 149 | } |
| 113 | 150 | |
| 114 | 151 | switch (code->op) { |
| @@ -129,6 +166,10 @@ stage3: · 14 unmodified lines | ||
| 129 | 166 | loc = *(u32 *)dest; |
| 130 | 167 | ret = fetch_store_string_user(val + code->offset, dest, base); |
| 131 | 168 | break; |
| 169 | + case FETCH_OP_ST_SYMSTR: | |
| 170 | + loc = *(u32 *)dest; | |
| 171 | + ret = fetch_store_symstring(val + code->offset, dest, base); | |
| 172 | + break; | |
| 132 | 173 | default: |
| 133 | 174 | return -EILSEQ; |
| 134 | 175 | } |