Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Support struct/union presets in veristat

Extend commit e3c9abd0d14b ("selftests/bpf: Implement setting global
variables in veristat") to support applying presets to members of
the global structs or unions in veristat.
For example:
```
./veristat set_global_vars.bpf.o -G "union1.struct3.var_u8_h = 0xBB"
```

Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250408104544.140317-1-mykyta.yatsenko5@gmail.com

authored by

Mykyta Yatsenko and committed by
Andrii Nakryiko
37b1b3ed c9661394

+140 -8
+5
tools/testing/selftests/bpf/prog_tests/test_veristat.c
··· 63 63 " -G \"var_eb = EB2\" "\ 64 64 " -G \"var_ec = EC2\" "\ 65 65 " -G \"var_b = 1\" "\ 66 + " -G \"struct1.struct2.u.var_u8 = 170\" "\ 67 + " -G \"union1.struct3.var_u8_l = 0xaa\" "\ 68 + " -G \"union1.struct3.var_u8_h = 0xaa\" "\ 66 69 "-vl2 > %s", fix->veristat, fix->tmpfile); 67 70 68 71 read(fix->fd, fix->output, fix->sz); ··· 81 78 __CHECK_STR("_w=12 ", "var_eb = EB2"); 82 79 __CHECK_STR("_w=13 ", "var_ec = EC2"); 83 80 __CHECK_STR("_w=1 ", "var_b = 1"); 81 + __CHECK_STR("_w=170 ", "struct1.struct2.u.var_u8 = 170"); 82 + __CHECK_STR("_w=0xaaaa ", "union1.var_u16 = 0xaaaa"); 84 83 85 84 out: 86 85 teardown_fixture(fix);
-1
tools/testing/selftests/bpf/progs/prepare.c
··· 2 2 /* Copyright (c) 2025 Meta */ 3 3 #include <vmlinux.h> 4 4 #include <bpf/bpf_helpers.h> 5 - //#include <bpf/bpf_tracing.h> 6 5 7 6 char _license[] SEC("license") = "GPL"; 8 7
+41
tools/testing/selftests/bpf/progs/set_global_vars.c
··· 24 24 const volatile enum Enums64 var_ec = EC1; 25 25 const volatile bool var_b = false; 26 26 27 + struct Struct { 28 + int:16; 29 + __u16 filler; 30 + struct { 31 + const __u16 filler2; 32 + }; 33 + struct Struct2 { 34 + __u16 filler; 35 + volatile struct { 36 + const int:1; 37 + union { 38 + const volatile __u8 var_u8; 39 + const volatile __s16 filler3; 40 + const int:1; 41 + } u; 42 + }; 43 + } struct2; 44 + }; 45 + 46 + const volatile __u32 stru = 0; /* same prefix as below */ 47 + const volatile struct Struct struct1 = {.struct2 = {.u = {.var_u8 = 1}}}; 48 + 49 + union Union { 50 + __u16 var_u16; 51 + struct Struct3 { 52 + struct { 53 + __u8 var_u8_l; 54 + }; 55 + struct { 56 + struct { 57 + __u8 var_u8_h; 58 + }; 59 + }; 60 + } struct3; 61 + }; 62 + 63 + const volatile union Union union1 = {.var_u16 = -1}; 64 + 27 65 char arr[4] = {0}; 28 66 29 67 SEC("socket") ··· 81 43 a = var_eb; 82 44 a = var_ec; 83 45 a = var_b; 46 + a = struct1.struct2.u.var_u8; 47 + a = union1.var_u16; 48 + 84 49 return a; 85 50 }
+94 -7
tools/testing/selftests/bpf/veristat.c
··· 1486 1486 return btf_is_int(t) || btf_is_enum(t) || btf_is_enum64(t); 1487 1487 } 1488 1488 1489 - static int set_global_var(struct bpf_object *obj, struct btf *btf, const struct btf_type *t, 1489 + const int btf_find_member(const struct btf *btf, 1490 + const struct btf_type *parent_type, 1491 + __u32 parent_offset, 1492 + const char *member_name, 1493 + int *member_tid, 1494 + __u32 *member_offset) 1495 + { 1496 + int i; 1497 + 1498 + if (!btf_is_composite(parent_type)) 1499 + return -EINVAL; 1500 + 1501 + for (i = 0; i < btf_vlen(parent_type); ++i) { 1502 + const struct btf_member *member; 1503 + const struct btf_type *member_type; 1504 + int tid; 1505 + 1506 + member = btf_members(parent_type) + i; 1507 + tid = btf__resolve_type(btf, member->type); 1508 + if (tid < 0) 1509 + return -EINVAL; 1510 + 1511 + member_type = btf__type_by_id(btf, tid); 1512 + if (member->name_off) { 1513 + const char *name = btf__name_by_offset(btf, member->name_off); 1514 + 1515 + if (strcmp(member_name, name) == 0) { 1516 + if (btf_member_bitfield_size(parent_type, i) != 0) { 1517 + fprintf(stderr, "Bitfield presets are not supported %s\n", 1518 + name); 1519 + return -EINVAL; 1520 + } 1521 + *member_offset = parent_offset + member->offset; 1522 + *member_tid = tid; 1523 + return 0; 1524 + } 1525 + } else if (btf_is_composite(member_type)) { 1526 + int err; 1527 + 1528 + err = btf_find_member(btf, member_type, parent_offset + member->offset, 1529 + member_name, member_tid, member_offset); 1530 + if (!err) 1531 + return 0; 1532 + } 1533 + } 1534 + 1535 + return -EINVAL; 1536 + } 1537 + 1538 + static int adjust_var_secinfo(struct btf *btf, const struct btf_type *t, 1539 + struct btf_var_secinfo *sinfo, const char *var) 1540 + { 1541 + char expr[256], *saveptr; 1542 + const struct btf_type *base_type, *member_type; 1543 + int err, member_tid; 1544 + char *name; 1545 + __u32 member_offset = 0; 1546 + 1547 + base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type)); 1548 + snprintf(expr, sizeof(expr), "%s", var); 1549 + strtok_r(expr, ".", &saveptr); 1550 + 1551 + while ((name = strtok_r(NULL, ".", &saveptr))) { 1552 + err = btf_find_member(btf, base_type, 0, name, &member_tid, &member_offset); 1553 + if (err) { 1554 + fprintf(stderr, "Could not find member %s for variable %s\n", name, var); 1555 + return err; 1556 + } 1557 + member_type = btf__type_by_id(btf, member_tid); 1558 + sinfo->offset += member_offset / 8; 1559 + sinfo->size = member_type->size; 1560 + sinfo->type = member_tid; 1561 + base_type = member_type; 1562 + } 1563 + return 0; 1564 + } 1565 + 1566 + static int set_global_var(struct bpf_object *obj, struct btf *btf, 1490 1567 struct bpf_map *map, struct btf_var_secinfo *sinfo, 1491 1568 struct var_preset *preset) 1492 1569 { ··· 1572 1495 long long value = preset->ivalue; 1573 1496 size_t size; 1574 1497 1575 - base_type = btf__type_by_id(btf, btf__resolve_type(btf, t->type)); 1498 + base_type = btf__type_by_id(btf, btf__resolve_type(btf, sinfo->type)); 1576 1499 if (!base_type) { 1577 - fprintf(stderr, "Failed to resolve type %d\n", t->type); 1500 + fprintf(stderr, "Failed to resolve type %d\n", sinfo->type); 1578 1501 return -EINVAL; 1579 1502 } 1580 1503 if (!is_preset_supported(base_type)) { ··· 1607 1530 if (value >= max_val || value < -max_val) { 1608 1531 fprintf(stderr, 1609 1532 "Variable %s value %lld is out of range [%lld; %lld]\n", 1610 - btf__name_by_offset(btf, t->name_off), value, 1533 + btf__name_by_offset(btf, base_type->name_off), value, 1611 1534 is_signed ? -max_val : 0, max_val - 1); 1612 1535 return -EINVAL; 1613 1536 } ··· 1660 1583 for (j = 0; j < n; ++j, ++sinfo) { 1661 1584 const struct btf_type *var_type = btf__type_by_id(btf, sinfo->type); 1662 1585 const char *var_name; 1586 + int var_len; 1663 1587 1664 1588 if (!btf_is_var(var_type)) 1665 1589 continue; 1666 1590 1667 1591 var_name = btf__name_by_offset(btf, var_type->name_off); 1592 + var_len = strlen(var_name); 1668 1593 1669 1594 for (k = 0; k < npresets; ++k) { 1670 - if (strcmp(var_name, presets[k].name) != 0) 1595 + struct btf_var_secinfo tmp_sinfo; 1596 + 1597 + if (strncmp(var_name, presets[k].name, var_len) != 0 || 1598 + (presets[k].name[var_len] != '\0' && 1599 + presets[k].name[var_len] != '.')) 1671 1600 continue; 1672 1601 1673 1602 if (presets[k].applied) { ··· 1681 1598 var_name); 1682 1599 return -EINVAL; 1683 1600 } 1601 + tmp_sinfo = *sinfo; 1602 + err = adjust_var_secinfo(btf, var_type, 1603 + &tmp_sinfo, presets[k].name); 1604 + if (err) 1605 + return err; 1684 1606 1685 - err = set_global_var(obj, btf, var_type, map, sinfo, presets + k); 1607 + err = set_global_var(obj, btf, map, &tmp_sinfo, presets + k); 1686 1608 if (err) 1687 1609 return err; 1688 1610 1689 1611 presets[k].applied = true; 1690 - break; 1691 1612 } 1692 1613 } 1693 1614 }