From: Mykyta Yatsenko <yatsenko@xxxxxxxx> Implement support for presetting values for array elements in veristat. For example: ``` sudo ./veristat set_global_vars.bpf.o -G "arr[3] = 1" ``` Arrays of structures and structure of arrays work, but each individual scalar value has to be set separately: `foo[1].bar[2] = value`. Signed-off-by: Mykyta Yatsenko <yatsenko@xxxxxxxx> --- tools/testing/selftests/bpf/veristat.c | 164 +++++++++++++++++++++---- 1 file changed, 142 insertions(+), 22 deletions(-) diff --git a/tools/testing/selftests/bpf/veristat.c b/tools/testing/selftests/bpf/veristat.c index 8291de199aab..bc9ebf5a2985 100644 --- a/tools/testing/selftests/bpf/veristat.c +++ b/tools/testing/selftests/bpf/veristat.c @@ -165,6 +165,7 @@ struct variant { struct var_preset_atom { char *name; + struct variant index; }; struct var_preset { @@ -1404,7 +1405,8 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf static int parse_var_atoms(const char *full_var, struct var_preset *preset) { - char expr[256], *name, *saveptr; + char expr[256], var[256], idx[256], *name, *saveptr; + int n, err; snprintf(expr, sizeof(expr), "%s", full_var); preset->atom_count = 0; @@ -1419,9 +1421,25 @@ static int parse_var_atoms(const char *full_var, struct var_preset *preset) preset->atoms = tmp; preset->atom_count++; - preset->atoms[i].name = strdup(name); - if (!preset->atoms[i].name) - return -ENOMEM; + if (sscanf(name, "%[a-zA-Z0-9_][%[a-zA-Z0-9]] %n", var, idx, &n) == 2 && + strlen(name) == n) { + /* current atom is an array, parse index */ + preset->atoms[i].name = strdup(var); + if (!preset->atoms[i].name) + return -ENOMEM; + err = parse_variant(idx, &preset->atoms[i].index); + if (err) + return err; + } else if (sscanf(name, "%[a-zA-Z0-9_] %n", var, &n) == 1 && + strlen(name) == n) { + preset->atoms[i].name = strdup(name); + if (!preset->atoms[i].name) + return -ENOMEM; + preset->atoms[i].index.type = NONE; + } else { + fprintf(stderr, "Could not parse '%s'", name); + return -EINVAL; + } } return 0; } @@ -1441,7 +1459,7 @@ static int append_var_preset(struct var_preset **presets, int *cnt, const char * memset(cur, 0, sizeof(*cur)); (*cnt)++; - if (sscanf(expr, "%s = %s %n", var, val, &n) != 2 || n != strlen(expr)) { + if (sscanf(expr, "%[][a-zA-Z0-9_.] = %s %n", var, val, &n) != 2 || n != strlen(expr)) { fprintf(stderr, "Failed to parse expression '%s'\n", expr); return -EINVAL; } @@ -1534,6 +1552,75 @@ static bool is_preset_supported(const struct btf_type *t) return btf_is_int(t) || btf_is_enum(t) || btf_is_enum64(t); } +static int find_enum_value(const struct btf *btf, const char *name, long long *value) +{ + const struct btf_type *t; + int cnt, i; + long long lvalue; + + cnt = btf__type_cnt(btf); + for (i = 1; i != cnt; ++i) { + t = btf__type_by_id(btf, i); + + if (!btf_is_any_enum(t)) + continue; + + if (enum_value_from_name(btf, t, name, &lvalue) == 0) { + *value = lvalue; + return 0; + } + } + return -ESRCH; +} + +static int adjust_array_secinfo(const struct btf *btf, const struct btf_type *t, + const struct var_preset_atom *var_atom, + struct btf_var_secinfo *sinfo) +{ + struct btf_array *barr; + const struct btf_type *type; + long long index; + int tid; + + if (!btf_is_array(t)) + return -EINVAL; + + barr = btf_array(t); + tid = btf__resolve_type(btf, barr->type); + type = btf__type_by_id(btf, tid); + if (!btf_is_int(type) && !btf_is_any_enum(type) && !btf_is_composite(type)) { + fprintf(stderr, + "Unsupported array element type for variable %s. Only int, enum, struct, union are supported\n", + var_atom->name); + return -EINVAL; + } + switch (var_atom->index.type) { + case INTEGRAL: + index = var_atom->index.ivalue; + break; + case ENUMERATOR: + if (find_enum_value(btf, var_atom->index.svalue, &index) != 0) { + fprintf(stderr, "Can't resolve %s enum as an array index", + var_atom->index.svalue); + return -EINVAL; + } + break; + case NONE: + fprintf(stderr, "Array index is expected for %s\n", var_atom->name); + return -EINVAL; + } + + if (index < 0 || index >= barr->nelems) { + fprintf(stderr, "Preset index %lld is invalid or out of bounds [0, %u]\n", + index, barr->nelems); + return -EINVAL; + } + sinfo->size = type->size; + sinfo->type = tid; + sinfo->offset += index * type->size; + return 0; +} + const int btf_find_member(const struct btf *btf, const struct btf_type *parent_type, __u32 parent_offset, @@ -1541,7 +1628,7 @@ const int btf_find_member(const struct btf *btf, int *member_tid, __u32 *member_offset) { - int i; + int i, err; if (!btf_is_composite(parent_type)) return -EINVAL; @@ -1550,37 +1637,57 @@ const int btf_find_member(const struct btf *btf, const struct btf_member *member; const struct btf_type *member_type; int tid; + const char *name; + u32 offset; member = btf_members(parent_type) + i; tid = btf__resolve_type(btf, member->type); if (tid < 0) return -EINVAL; + name = btf__name_by_offset(btf, member->name_off); + if (name[0] != '\0' && strcmp(var_atom->name, name) != 0) + continue; + + if (btf_member_bitfield_size(parent_type, i) != 0) { + fprintf(stderr, "Bitfield presets are not supported %s\n", + name); + return -EINVAL; + } + member_type = btf__type_by_id(btf, tid); - if (member->name_off) { - const char *name = btf__name_by_offset(btf, member->name_off); + offset = parent_offset + member->offset; - if (strcmp(var_atom->name, name) == 0) { - if (btf_member_bitfield_size(parent_type, i) != 0) { - fprintf(stderr, "Bitfield presets are not supported %s\n", - name); - return -EINVAL; - } - *member_offset = parent_offset + member->offset; - *member_tid = tid; - return 0; - } - } else if (btf_is_composite(member_type)) { + if (name[0] == '\0' && btf_is_composite(member_type)) { /* anon struct/union */ int err; - err = btf_find_member(btf, member_type, parent_offset + member->offset, + err = btf_find_member(btf, member_type, offset, var_atom, member_tid, member_offset); if (!err) return 0; + } else if (name[0] && btf_is_array(member_type)) { + struct btf_var_secinfo sinfo = {.offset = 0}; + + err = adjust_array_secinfo(btf, member_type, var_atom, &sinfo); + if (err) + return err; + + *member_tid = sinfo.type; + *member_offset = offset + sinfo.offset * 8; + return 0; + } else if (name[0]) { + if (var_atom->index.type != NONE) { + fprintf(stderr, "Array index is not expected for %s\n", name); + return -EINVAL; + } + + *member_offset = offset; + *member_tid = tid; + return 0; } } - return -EINVAL; + return -ESRCH; } static int adjust_var_secinfo(struct btf *btf, const struct btf_type *t, @@ -1591,6 +1698,15 @@ static int adjust_var_secinfo(struct btf *btf, const struct btf_type *t, __u32 member_offset = 0; base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type)); + if (btf_is_array(base_type)) { + err = adjust_array_secinfo(btf, base_type, &preset->atoms[0], sinfo); + if (err) + return err; + base_type = btf__type_by_id(btf, sinfo->type); + } else if (preset->atoms[0].index.type != NONE) { + fprintf(stderr, "Array index is not expected for %s\n", preset->atoms[0].name); + return -EINVAL; + } for (i = 1; i < preset->atom_count; ++i) { err = btf_find_member(btf, base_type, 0, &preset->atoms[i], @@ -1742,6 +1858,7 @@ static int set_global_vars(struct bpf_object *obj, struct var_preset *presets, i if (!presets[i].applied) { fprintf(stderr, "Global variable preset %s has not been applied\n", presets[i].full_name); + err = -EINVAL; } presets[i].applied = false; } @@ -2929,8 +3046,11 @@ int main(int argc, char **argv) free(env.deny_filters); for (i = 0; i < env.npresets; ++i) { free(env.presets[i].full_name); - for (j = 0; j < env.presets[i].atom_count; ++j) + for (j = 0; j < env.presets[i].atom_count; ++j) { free(env.presets[i].atoms[j].name); + if (env.presets[i].atoms[j].index.type == ENUMERATOR) + free(env.presets[i].atoms[j].index.svalue); + } free(env.presets[i].atoms); if (env.presets[i].value.type == ENUMERATOR) free(env.presets[i].value.svalue); -- 2.49.0