From e86ebb7affc6ef5bded822575597b39bab089852 Mon Sep 17 00:00:00 2001 From: Alastair Robertson Date: Thu, 29 Jun 2023 01:15:03 -0700 Subject: [PATCH] TypeGraph: Support bitfields - Change member and parent offsets to work in bits, not bytes - Printer still displays offsets in bytes, with decimals when using bitfields - AddPadding: Don't pad bitfields - CodeGen: Emit code for bitfields --- oi/CodeGen.cpp | 13 +++- oi/type_graph/AddPadding.cpp | 34 ++++++--- oi/type_graph/AddPadding.h | 2 +- oi/type_graph/DrgnParser.cpp | 18 ++--- oi/type_graph/Flattener.cpp | 9 ++- oi/type_graph/Printer.cpp | 12 ++- oi/type_graph/RemoveIgnored.cpp | 3 +- oi/type_graph/Types.h | 15 ++-- test/integration/bitfields.toml | 125 ++++++++++++++++++++++++++++++++ test/test_add_padding.cpp | 59 ++++++++++++++- test/test_alignment_calc.cpp | 10 +-- test/test_drgn_parser.cpp | 84 +++++++++++++++++++++ test/test_flattener.cpp | 92 +++++++++++++---------- test/test_remove_ignored.cpp | 12 +-- 14 files changed, 398 insertions(+), 90 deletions(-) create mode 100644 test/integration/bitfields.toml diff --git a/oi/CodeGen.cpp b/oi/CodeGen.cpp index 518e9e3..928ee5f 100644 --- a/oi/CodeGen.cpp +++ b/oi/CodeGen.cpp @@ -247,7 +247,11 @@ void genDefsClass(const Class& c, std::string& code) { code += c.name() + " {\n"; for (const auto& mem : c.members) { - code += " " + mem.type->name() + " " + mem.name + ";\n"; + code += " " + mem.type->name() + " " + mem.name; + if (mem.bitsize) { + code += " : " + std::to_string(mem.bitsize); + } + code += ";\n"; } code += "};\n\n"; } @@ -271,8 +275,10 @@ void genStaticAssertsClass(const Class& c, std::string& code) { ") == " + std::to_string(c.size()) + ", \"Unexpected size of struct " + c.name() + "\");\n"; for (const auto& member : c.members) { + if (member.bitsize > 0) + continue; code += "static_assert(offsetof(" + c.name() + ", " + member.name + - ") == " + std::to_string(member.offset) + + ") == " + std::to_string(member.bitOffset / 8) + ", \"Unexpected offset of " + c.name() + "::" + member.name + "\");\n"; } @@ -407,7 +413,8 @@ void CodeGen::getClassSizeFuncConcrete(std::string_view funcName, } code += " JLOG(\"" + member.name + " @\");\n"; - code += " JLOGPTR(&t." + member.name + ");\n"; + if (member.bitsize == 0) + code += " JLOGPTR(&t." + member.name + ");\n"; code += " getSizeType(t." + member.name + ", returnArg);\n"; } code += "}\n"; diff --git a/oi/type_graph/AddPadding.cpp b/oi/type_graph/AddPadding.cpp index 6df0c4f..fca93c1 100644 --- a/oi/type_graph/AddPadding.cpp +++ b/oi/type_graph/AddPadding.cpp @@ -65,13 +65,13 @@ void AddPadding::visit(Class& c) { paddedMembers.reserve(c.members.size()); for (size_t i = 0; i < c.members.size(); i++) { if (i >= 1) { - addPadding(c.members[i - 1], c.members[i].offset, paddedMembers); + addPadding(c.members[i - 1], c.members[i].bitOffset, paddedMembers); } paddedMembers.push_back(c.members[i]); } if (!c.members.empty()) { - addPadding(c.members.back(), c.size(), paddedMembers); + addPadding(c.members.back(), c.size() * 8, paddedMembers); } c.members = std::move(paddedMembers); @@ -82,16 +82,32 @@ void AddPadding::visit(Class& c) { } void AddPadding::addPadding(const Member& prevMember, - uint64_t paddingEnd, + uint64_t paddingEndBits, std::vector& paddedMembers) { - uint64_t prevMemberEnd = prevMember.offset + prevMember.type->size(); - uint64_t padding = paddingEnd - prevMemberEnd; - if (padding == 0) + uint64_t prevMemberSizeBits; + if (prevMember.bitsize == 0) { + prevMemberSizeBits = prevMember.type->size() * 8; + } else { + prevMemberSizeBits = prevMember.bitsize; + } + + uint64_t prevMemberEndBits = prevMember.bitOffset + prevMemberSizeBits; + uint64_t paddingBits = paddingEndBits - prevMemberEndBits; + if (paddingBits == 0) return; - auto* primitive = typeGraph_.make_type(Primitive::Kind::Int8); - auto* paddingArray = typeGraph_.make_type(primitive, padding); - paddedMembers.emplace_back(paddingArray, MemberPrefix, prevMemberEnd); + if (paddingBits % 8 == 0) { + // Pad with an array of bytes + auto* primitive = typeGraph_.make_type(Primitive::Kind::Int8); + auto* paddingArray = + typeGraph_.make_type(primitive, paddingBits / 8); + paddedMembers.emplace_back(paddingArray, MemberPrefix, prevMemberEndBits); + } else { + // Pad with a bitfield + auto* primitive = typeGraph_.make_type(Primitive::Kind::Int64); + paddedMembers.emplace_back(primitive, MemberPrefix, prevMemberEndBits, + paddingBits); + } } } // namespace type_graph diff --git a/oi/type_graph/AddPadding.h b/oi/type_graph/AddPadding.h index 23ee4ea..dec7302 100644 --- a/oi/type_graph/AddPadding.h +++ b/oi/type_graph/AddPadding.h @@ -45,7 +45,7 @@ class AddPadding final : public RecursiveVisitor { void visit(Type& type) override; void visit(Class& c) override; - static const inline std::string MemberPrefix = "__oid_padding"; + static const inline std::string MemberPrefix = "__oi_padding"; private: std::unordered_set visited_; diff --git a/oi/type_graph/DrgnParser.cpp b/oi/type_graph/DrgnParser.cpp index 20957c6..b0661b3 100644 --- a/oi/type_graph/DrgnParser.cpp +++ b/oi/type_graph/DrgnParser.cpp @@ -212,13 +212,14 @@ void DrgnParser::enumerateClassParents(struct drgn_type* type, } auto ptype = enumerateType(parent_qual_type.type); - uint64_t poffset = drgn_parents[i].bit_offset / 8; + uint64_t poffset = drgn_parents[i].bit_offset; Parent p(ptype, poffset); parents.push_back(p); } - std::sort(parents.begin(), parents.end(), - [](const auto& a, const auto& b) { return a.offset < b.offset; }); + std::sort(parents.begin(), parents.end(), [](const auto& a, const auto& b) { + return a.bitOffset < b.bitOffset; + }); } void DrgnParser::enumerateClassMembers(struct drgn_type* type, @@ -257,17 +258,16 @@ void DrgnParser::enumerateClassMembers(struct drgn_type* type, if (drgn_members[i].name) member_name = drgn_members[i].name; - // TODO bitfields - auto mtype = enumerateType(member_type); - uint64_t moffset = drgn_members[i].bit_offset / 8; + uint64_t moffset = drgn_members[i].bit_offset; - Member m(mtype, member_name, moffset); // TODO + Member m{mtype, member_name, moffset, bit_field_size}; members.push_back(m); } - std::sort(members.begin(), members.end(), - [](const auto& a, const auto& b) { return a.offset < b.offset; }); + std::sort(members.begin(), members.end(), [](const auto& a, const auto& b) { + return a.bitOffset < b.bitOffset; + }); } void DrgnParser::enumerateTemplateParam(drgn_type_template_parameter* tparams, diff --git a/oi/type_graph/Flattener.cpp b/oi/type_graph/Flattener.cpp index 3e38391..3aba138 100644 --- a/oi/type_graph/Flattener.cpp +++ b/oi/type_graph/Flattener.cpp @@ -62,7 +62,7 @@ void flattenParent(const Parent& parent, for (size_t i = 0; i < parentClass->members.size(); i++) { const auto& member = parentClass->members[i]; flattenedMembers.push_back(member); - flattenedMembers.back().offset += parent.offset; + flattenedMembers.back().bitOffset += parent.bitOffset; if (i == 0) { flattenedMembers.back().align = std::max(flattenedMembers.back().align, parentClass->align()); @@ -71,7 +71,8 @@ void flattenParent(const Parent& parent, } else if (Container* parentContainer = dynamic_cast(&parentType)) { // Create a new member to represent this parent container - flattenedMembers.emplace_back(parentContainer, "__parent", parent.offset); + flattenedMembers.emplace_back(parentContainer, "__parent", + parent.bitOffset); } else { throw std::runtime_error("Invalid type for parent"); } @@ -169,8 +170,8 @@ void Flattener::visit(Class& c) { std::size_t member_idx = 0; std::size_t parent_idx = 0; while (member_idx < c.members.size() && parent_idx < c.parents.size()) { - auto member_offset = c.members[member_idx].offset; - auto parent_offset = c.parents[parent_idx].offset; + auto member_offset = c.members[member_idx].bitOffset; + auto parent_offset = c.parents[parent_idx].bitOffset; if (member_offset < parent_offset) { // Add our own member const auto& member = c.members[member_idx++]; diff --git a/oi/type_graph/Printer.cpp b/oi/type_graph/Printer.cpp index 6784b4c..9013afa 100644 --- a/oi/type_graph/Printer.cpp +++ b/oi/type_graph/Printer.cpp @@ -166,7 +166,8 @@ void Printer::print_param(const TemplateParam& param) { void Printer::print_parent(const Parent& parent) { depth_++; prefix(); - out_ << "Parent (offset: " << parent.offset << ")" << std::endl; + out_ << "Parent (offset: " << static_cast(parent.bitOffset) / 8 << ")" + << std::endl; print(*parent.type); depth_--; } @@ -174,8 +175,13 @@ void Printer::print_parent(const Parent& parent) { void Printer::print_member(const Member& member) { depth_++; prefix(); - out_ << "Member: " << member.name << " (offset: " << member.offset - << align_str(member.align) << ")" << std::endl; + out_ << "Member: " << member.name + << " (offset: " << static_cast(member.bitOffset) / 8; + out_ << align_str(member.align); + if (member.bitsize != 0) { + out_ << ", bitsize: " << member.bitsize; + } + out_ << ")" << std::endl; print(*member.type); depth_--; } diff --git a/oi/type_graph/RemoveIgnored.cpp b/oi/type_graph/RemoveIgnored.cpp index 5fdf21b..9c67d52 100644 --- a/oi/type_graph/RemoveIgnored.cpp +++ b/oi/type_graph/RemoveIgnored.cpp @@ -47,7 +47,8 @@ void RemoveIgnored::visit(Class& c) { auto* primitive = typeGraph_.make_type(Primitive::Kind::Int8); auto* paddingArray = typeGraph_.make_type(primitive, c.members[i].type->size()); - c.members[i] = Member{paddingArray, c.members[i].name, c.members[i].offset}; + c.members[i] = + Member{paddingArray, c.members[i].name, c.members[i].bitOffset}; } } diff --git a/oi/type_graph/Types.h b/oi/type_graph/Types.h index eee9ad0..fce882b 100644 --- a/oi/type_graph/Types.h +++ b/oi/type_graph/Types.h @@ -69,14 +69,15 @@ class Type { struct Member { Member(Type* type, const std::string& name, - uint64_t offset, - uint64_t align = 0) - : type(type), name(name), offset(offset), align(align) { + uint64_t bitOffset, + uint64_t bitsize = 0) + : type(type), name(name), bitOffset(bitOffset), bitsize(bitsize) { } Type* type; - std::string name; // TODO make optional? - uint64_t offset; + std::string name; + uint64_t bitOffset; + uint64_t bitsize; uint64_t align = 0; }; @@ -91,11 +92,11 @@ struct Function { class Class; struct Parent { - Parent(Type* type, uint64_t offset) : type(type), offset(offset) { + Parent(Type* type, uint64_t bitOffset) : type(type), bitOffset(bitOffset) { } Type* type; - uint64_t offset; + uint64_t bitOffset; }; struct TemplateParam { diff --git a/test/integration/bitfields.toml b/test/integration/bitfields.toml new file mode 100644 index 0000000..bd1e052 --- /dev/null +++ b/test/integration/bitfields.toml @@ -0,0 +1,125 @@ +definitions = ''' + struct Single { + int bitfield : 3; + }; + + struct WithinBytes { + char a : 3; + char b : 5; + char c : 7; + }; + + struct StraddleBytes { + char a : 7; + char b : 7; + char c : 2; + }; + + struct Mixed { + int a; + char b : 4; + short c : 12; + char d; + int e : 22; + }; + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wbitfield-width" + // The bitfield will max out at the size of its type. Extra bits act as padding. + struct MoreBitsThanType { + char a : 29; + }; + #pragma clang diagnostic pop + + // A zero-sized bitfield adds default padding between neighbouring bitfields + struct ZeroBits { + char b1 : 3; + char : 0; + char b2 : 2; + }; + + enum class MyEnum { + One, + Two, + Three, + }; + + struct Enum { + MyEnum e : 2; + MyEnum f : 4; + }; +''' +# TODO The sizes do not take bitfields into account. They count each field as +# if they were regular primitives. +[cases] + [cases.single] + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["Single&"] + setup = "return {};" + expect_json = '''[ + {"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "members":[ + {"staticSize":4, "dynamicSize":0, "exclusiveSize":4} + ]}]''' + [cases.within_bytes] + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["WithinBytes&"] + setup = "return {};" + expect_json = '''[ + {"staticSize":2, "dynamicSize":0, "exclusiveSize":0, "members":[ + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1} + ]}]''' + [cases.straddle_bytes] + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["StraddleBytes&"] + setup = "return {};" + expect_json = '''[ + {"staticSize":3, "dynamicSize":0, "exclusiveSize":0, "members":[ + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1} + ]}]''' + [cases.mixed] + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["Mixed&"] + setup = "return {};" + expect_json = '''[ + {"staticSize":12, "dynamicSize":0, "exclusiveSize":0, "members":[ + {"staticSize":4, "dynamicSize":0, "exclusiveSize":4}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":2, "dynamicSize":0, "exclusiveSize":2}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":4, "dynamicSize":0, "exclusiveSize":4} + ]}]''' + [cases.more_bits_than_type] # TODO member sizes are wrong + skip = "drgn errors out" + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["MoreBitsThanType&"] + setup = "return {};" + expect_json = '"TODO"' + [cases.zero_bits] + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["ZeroBits&"] + setup = "return {};" + expect_json = '''[ + {"staticSize":2, "dynamicSize":0, "exclusiveSize":0, "members":[ + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1}, + {"staticSize":1, "dynamicSize":0, "exclusiveSize":1} + ]}]''' + [cases.enum] + cli_options = ["-ftype-graph"] + oil_skip = "not implemented" + param_types = ["Enum&"] + setup = "return {};" + expect_json = '''[ + {"staticSize":4, "dynamicSize":0, "exclusiveSize":0, "members":[ + {"staticSize":4, "dynamicSize":0, "exclusiveSize":4}, + {"staticSize":4, "dynamicSize":0, "exclusiveSize":4} + ]}]''' diff --git a/test/test_add_padding.cpp b/test/test_add_padding.cpp index 747d81b..19974e5 100644 --- a/test/test_add_padding.cpp +++ b/test/test_add_padding.cpp @@ -11,13 +11,13 @@ TEST(AddPaddingTest, BetweenMembers) { auto myint8 = Primitive{Primitive::Kind::Int8}; auto myint64 = Primitive{Primitive::Kind::Int64}; myclass.members.push_back(Member(&myint8, "n1", 0)); - myclass.members.push_back(Member(&myint64, "n2", 8)); + myclass.members.push_back(Member(&myint64, "n2", 8 * 8)); test(AddPadding::createPass(), {myclass}, R"( [0] Class: MyClass (size: 16) Member: n1 (offset: 0) Primitive: int8_t - Member: __oid_padding (offset: 1) + Member: __oi_padding (offset: 1) [1] Array: (length: 7) Primitive: int8_t Member: n2 (offset: 8) @@ -30,7 +30,7 @@ TEST(AddPaddingTest, AtEnd) { auto myint8 = Primitive{Primitive::Kind::Int8}; auto myint64 = Primitive{Primitive::Kind::Int64}; myclass.members.push_back(Member(&myint64, "n1", 0)); - myclass.members.push_back(Member(&myint8, "n2", 8)); + myclass.members.push_back(Member(&myint8, "n2", 8 * 8)); test(AddPadding::createPass(), {myclass}, R"( [0] Struct: MyStruct (size: 16) @@ -38,7 +38,7 @@ TEST(AddPaddingTest, AtEnd) { Primitive: int64_t Member: n2 (offset: 8) Primitive: int8_t - Member: __oid_padding (offset: 9) + Member: __oi_padding (offset: 9) [1] Array: (length: 7) Primitive: int8_t )"); @@ -60,4 +60,55 @@ TEST(AddPaddingTest, UnionNotPadded) { )"); } +TEST(AddPaddingTest, Bitfields) { + auto myclass = Class{Class::Kind::Class, "MyClass", 16}; + auto myint64 = Primitive{Primitive::Kind::Int64}; + auto myint16 = Primitive{Primitive::Kind::Int16}; + auto myint8 = Primitive{Primitive::Kind::Int8}; + + Member b1{&myint64, "b1", 0}; + b1.bitsize = 3; + Member b2{&myint8, "b2", 3}; + b2.bitsize = 2; + // There may be a 0-sized bitfield between these two that does not appear + // in the DWARF. This would add padding and push b3 to the next byte. + Member b3{&myint8, "b3", 8}; + b3.bitsize = 1; + + Member b4{&myint64, "b4", 8 * 8}; + b4.bitsize = 24; + + Member n{&myint16, "n", 12 * 8}; + + myclass.members.push_back(b1); + myclass.members.push_back(b2); + myclass.members.push_back(b3); + myclass.members.push_back(b4); + myclass.members.push_back(n); + + test(AddPadding::createPass(), {myclass}, R"( +[0] Class: MyClass (size: 16) + Member: b1 (offset: 0, bitsize: 3) + Primitive: int64_t + Member: b2 (offset: 0.375, bitsize: 2) + Primitive: int8_t + Member: __oi_padding (offset: 0.625, bitsize: 3) + Primitive: int64_t + Member: b3 (offset: 1, bitsize: 1) + Primitive: int8_t + Member: __oi_padding (offset: 1.125, bitsize: 55) + Primitive: int64_t + Member: b4 (offset: 8, bitsize: 24) + Primitive: int64_t + Member: __oi_padding (offset: 11) +[1] Array: (length: 1) + Primitive: int8_t + Member: n (offset: 12) + Primitive: int16_t + Member: __oi_padding (offset: 14) +[2] Array: (length: 2) + Primitive: int8_t +)"); +} + // TODO test we follow class members, templates, children diff --git a/test/test_alignment_calc.cpp b/test/test_alignment_calc.cpp index 73f0db9..2627b3b 100644 --- a/test/test_alignment_calc.cpp +++ b/test/test_alignment_calc.cpp @@ -10,7 +10,7 @@ TEST(AlignmentCalcTest, PrimitiveMembers) { auto myint8 = Primitive{Primitive::Kind::Int8}; auto myint64 = Primitive{Primitive::Kind::Int64}; myclass.members.push_back(Member(&myint8, "n", 0)); - myclass.members.push_back(Member(&myint64, "n", 8)); + myclass.members.push_back(Member(&myint64, "n", 8 * 8)); test(AlignmentCalc::createPass(), {myclass}, R"( [0] Class: MyClass (size: 16, align: 8) @@ -25,12 +25,12 @@ TEST(AlignmentCalcTest, StructMembers) { auto mystruct = Class{Class::Kind::Struct, "MyStruct", 8}; auto myint32 = Primitive{Primitive::Kind::Int32}; mystruct.members.push_back(Member(&myint32, "n1", 0)); - mystruct.members.push_back(Member(&myint32, "n2", 4)); + mystruct.members.push_back(Member(&myint32, "n2", 4 * 8)); auto myclass = Class{Class::Kind::Class, "MyClass", 12}; auto myint8 = Primitive{Primitive::Kind::Int8}; myclass.members.push_back(Member(&myint8, "n", 0)); - myclass.members.push_back(Member(&mystruct, "s", 4)); + myclass.members.push_back(Member(&mystruct, "s", 4 * 8)); test(AlignmentCalc::createPass(), {myclass}, R"( [0] Class: MyClass (size: 12, align: 4) @@ -50,7 +50,7 @@ TEST(AlignmentCalcTest, StructInContainer) { auto myint8 = Primitive{Primitive::Kind::Int8}; auto myint64 = Primitive{Primitive::Kind::Int64}; myclass.members.push_back(Member(&myint8, "n", 0)); - myclass.members.push_back(Member(&myint64, "n", 8)); + myclass.members.push_back(Member(&myint64, "n", 8 * 8)); auto mycontainer = Container{ContainerInfo{}, 8}; mycontainer.templateParams.push_back(&myclass); @@ -71,7 +71,7 @@ TEST(AlignmentCalcTest, Packed) { auto myint8 = Primitive{Primitive::Kind::Int8}; auto myint64 = Primitive{Primitive::Kind::Int64}; mystruct.members.push_back(Member(&myint8, "n1", 0)); - mystruct.members.push_back(Member(&myint64, "n2", 1)); + mystruct.members.push_back(Member(&myint64, "n2", 1 * 8)); test(AlignmentCalc::createPass(), {mystruct}, R"( [0] Struct: MyStruct (size: 9, align: 8, packed) diff --git a/test/test_drgn_parser.cpp b/test/test_drgn_parser.cpp index 2c8f840..233b790 100644 --- a/test/test_drgn_parser.cpp +++ b/test/test_drgn_parser.cpp @@ -581,4 +581,88 @@ TEST_F(DrgnParserTest, VirtualFunctions) { )"); } +TEST_F(DrgnParserTest, BitfieldsSingle) { + test("oid_test_case_bitfields_single", R"( +[0] Pointer +[1] Struct: Single (size: 4) + Member: bitfield (offset: 0, bitsize: 3) + Primitive: int32_t +)"); +} + +TEST_F(DrgnParserTest, BitfieldsWithinBytes) { + test("oid_test_case_bitfields_within_bytes", R"( +[0] Pointer +[1] Struct: WithinBytes (size: 2) + Member: a (offset: 0, bitsize: 3) + Primitive: int8_t + Member: b (offset: 0.375, bitsize: 5) + Primitive: int8_t + Member: c (offset: 1, bitsize: 7) + Primitive: int8_t +)"); +} + +TEST_F(DrgnParserTest, BitfieldsStraddleBytes) { + test("oid_test_case_bitfields_straddle_bytes", R"( +[0] Pointer +[1] Struct: StraddleBytes (size: 3) + Member: a (offset: 0, bitsize: 7) + Primitive: int8_t + Member: b (offset: 1, bitsize: 7) + Primitive: int8_t + Member: c (offset: 2, bitsize: 2) + Primitive: int8_t +)"); +} + +TEST_F(DrgnParserTest, BitfieldsMixed) { + test("oid_test_case_bitfields_mixed", R"( +[0] Pointer +[1] Struct: Mixed (size: 12) + Member: a (offset: 0) + Primitive: int32_t + Member: b (offset: 4, bitsize: 4) + Primitive: int8_t + Member: c (offset: 4.5, bitsize: 12) + Primitive: int16_t + Member: d (offset: 6) + Primitive: int8_t + Member: e (offset: 8, bitsize: 22) + Primitive: int32_t +)"); +} + +TEST_F(DrgnParserTest, BitfieldsMoreBitsThanType) { + GTEST_SKIP() << "drgn errors out"; + test("oid_test_case_bitfields_more_bits_than_type", R"( +[0] Pointer +[1] Struct: MoreBitsThanType (size: 4) + Member: a (offset: 0, bitsize: 8) + Primitive: int8_t +)"); +} + +TEST_F(DrgnParserTest, BitfieldsZeroBits) { + test("oid_test_case_bitfields_zero_bits", R"( +[0] Pointer +[1] Struct: ZeroBits (size: 2) + Member: b1 (offset: 0, bitsize: 3) + Primitive: int8_t + Member: b2 (offset: 1, bitsize: 2) + Primitive: int8_t +)"); +} + +TEST_F(DrgnParserTest, BitfieldsEnum) { + test("oid_test_case_bitfields_enum", R"( +[0] Pointer +[1] Struct: Enum (size: 4) + Member: e (offset: 0, bitsize: 2) + Enum: MyEnum (size: 4) + Member: f (offset: 0.25, bitsize: 4) + Enum: MyEnum (size: 4) +)"); +} + // TODO test virtual classes diff --git a/test/test_flattener.cpp b/test/test_flattener.cpp index 2147f0d..06260e7 100644 --- a/test/test_flattener.cpp +++ b/test/test_flattener.cpp @@ -22,8 +22,8 @@ TEST(FlattenerTest, NoParents) { mystruct.members.push_back(Member(&myint, "n0", 0)); myclass.members.push_back(Member(&myint, "n", 0)); - myclass.members.push_back(Member(&myenum, "e", 4)); - myclass.members.push_back(Member(&mystruct, "mystruct", 8)); + myclass.members.push_back(Member(&myenum, "e", 4 * 8)); + myclass.members.push_back(Member(&mystruct, "mystruct", 8 * 8)); test(Flattener::createPass(), {myclass}, R"( [0] Class: MyClass (size: 12) @@ -58,7 +58,7 @@ TEST(FlattenerTest, OnlyParents) { classB.members.push_back(Member(&myint, "b", 0)); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 4)); + classA.parents.push_back(Parent(&classC, 4 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 8) @@ -90,8 +90,8 @@ TEST(FlattenerTest, ParentsFirst) { classB.members.push_back(Member(&myint, "b", 0)); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 4)); - classA.members.push_back(Member(&myint, "a", 8)); + classA.parents.push_back(Parent(&classC, 4 * 8)); + classA.members.push_back(Member(&myint, "a", 8 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 12) @@ -126,8 +126,8 @@ TEST(FlattenerTest, MembersFirst) { classB.members.push_back(Member(&myint, "b", 0)); classA.members.push_back(Member(&myint, "a", 0)); - classA.parents.push_back(Parent(&classB, 4)); - classA.parents.push_back(Parent(&classC, 8)); + classA.parents.push_back(Parent(&classB, 4 * 8)); + classA.parents.push_back(Parent(&classC, 8 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 12) @@ -163,9 +163,9 @@ TEST(FlattenerTest, MixedMembersAndParents) { classB.members.push_back(Member(&myint, "b", 0)); classA.parents.push_back(Parent(&classB, 0)); - classA.members.push_back(Member(&myint, "a1", 4)); - classA.members.push_back(Member(&myint, "a2", 8)); - classA.parents.push_back(Parent(&classC, 12)); + classA.members.push_back(Member(&myint, "a1", 4 * 8)); + classA.members.push_back(Member(&myint, "a2", 8 * 8)); + classA.parents.push_back(Parent(&classC, 12 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 16) @@ -199,9 +199,9 @@ TEST(FlattenerTest, EmptyParent) { classC.members.push_back(Member(&myint, "c", 0)); - classA.members.push_back(Member(&myint, "a1", 4)); - classA.members.push_back(Member(&myint, "a2", 8)); - classA.parents.push_back(Parent(&classB, 4)); + classA.members.push_back(Member(&myint, "a1", 4 * 8)); + classA.members.push_back(Member(&myint, "a2", 8 * 8)); + classA.parents.push_back(Parent(&classB, 0)); classA.parents.push_back(Parent(&classC, 0)); test(Flattener::createPass(), {classA}, R"( @@ -240,11 +240,11 @@ TEST(FlattenerTest, TwoDeep) { classC.members.push_back(Member(&myint, "c", 0)); classB.parents.push_back(Parent(&classD, 0)); - classB.members.push_back(Member(&myint, "b", 4)); + classB.members.push_back(Member(&myint, "b", 4 * 8)); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 8)); - classA.members.push_back(Member(&myint, "a", 12)); + classA.parents.push_back(Parent(&classC, 8 * 8)); + classA.members.push_back(Member(&myint, "a", 12 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 16) @@ -280,11 +280,11 @@ TEST(FlattenerTest, DiamondInheritance) { classC.members.push_back(Member(&myint, "c", 0)); classB.parents.push_back(Parent(&classC, 0)); - classB.members.push_back(Member(&myint, "b", 4)); + classB.members.push_back(Member(&myint, "b", 4 * 8)); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 8)); - classA.members.push_back(Member(&myint, "a", 12)); + classA.parents.push_back(Parent(&classC, 8 * 8)); + classA.members.push_back(Member(&myint, "a", 12 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 16) @@ -316,10 +316,10 @@ TEST(FlattenerTest, Member) { classC.members.push_back(Member(&myint, "c", 0)); classB.parents.push_back(Parent(&classC, 0)); - classB.members.push_back(Member(&myint, "b", 4)); + classB.members.push_back(Member(&myint, "b", 4 * 8)); classA.members.push_back(Member(&myint, "a", 0)); - classA.members.push_back(Member(&classB, "b", 4)); + classA.members.push_back(Member(&classB, "b", 4 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 12) @@ -351,10 +351,10 @@ TEST(FlattenerTest, MemberOfParent) { classC.members.push_back(Member(&myint, "c", 0)); classB.members.push_back(Member(&myint, "b", 0)); - classB.members.push_back(Member(&classC, "c", 4)); + classB.members.push_back(Member(&classC, "c", 4 * 8)); classA.parents.push_back(Parent(&classB, 0)); - classA.members.push_back(Member(&myint, "a", 8)); + classA.members.push_back(Member(&myint, "a", 8 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 12) @@ -386,7 +386,7 @@ TEST(FlattenerTest, ContainerParam) { classB.members.push_back(Member(&myint, "b", 0)); classA.parents.push_back(Parent(&classB, 0)); - classA.members.push_back(Member(&myint, "a", 4)); + classA.members.push_back(Member(&myint, "a", 4 * 8)); container.templateParams.push_back(TemplateParam(&classA)); container.templateParams.push_back(TemplateParam(&myint)); @@ -416,7 +416,7 @@ TEST(FlattenerTest, Array) { auto classA = Class{Class::Kind::Class, "ClassA", 8}; classA.parents.push_back(Parent(&classB, 0)); - classA.members.push_back(Member(&myint, "a", 4)); + classA.members.push_back(Member(&myint, "a", 4 * 8)); auto arrayA = Array{&classA, 5}; @@ -441,7 +441,7 @@ TEST(FlattenerTest, Typedef) { auto classA = Class{Class::Kind::Class, "ClassA", 8}; classA.parents.push_back(Parent(&classB, 0)); - classA.members.push_back(Member(&myint, "a", 4)); + classA.members.push_back(Member(&myint, "a", 4 * 8)); auto aliasA = Typedef{"aliasA", &classA}; @@ -468,7 +468,7 @@ TEST(FlattenerTest, TypedefParent) { auto classA = Class{Class::Kind::Class, "ClassA", 8}; classA.parents.push_back(Parent(&aliasB, 0)); - classA.members.push_back(Member(&myint, "a", 4)); + classA.members.push_back(Member(&myint, "a", 4 * 8)); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 8) @@ -491,7 +491,7 @@ TEST(FlattenerTest, Pointer) { auto classA = Class{Class::Kind::Class, "ClassA", 8}; classA.parents.push_back(Parent(&classB, 0)); - classA.members.push_back(Member(&myint, "a", 4)); + classA.members.push_back(Member(&myint, "a", 4 * 8)); auto ptrA = Pointer{&classA}; auto classC = Class{Class::Kind::Class, "ClassC", 8}; @@ -545,13 +545,29 @@ TEST(FlattenerTest, Alignment) { classC.setAlign(16); classC.members.push_back(Member(&myint, "c", 0)); - classB.members.push_back(Member(&myint, "b", 0, 8)); + + Member memberB{&myint, "b", 0}; + memberB.align = 8; + classB.members.push_back(memberB); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 4)); - classA.members.push_back(Member(&myint, "a", 8)); + classA.parents.push_back(Parent(&classC, 4 * 8)); + classA.members.push_back(Member(&myint, "a", 8 * 8)); test(Flattener::createPass(), {classA}, R"( +[0] Class: ClassA (size: 12) + Parent (offset: 0) +[1] Class: ClassB (size: 4) + Member: b (offset: 0, align: 8) + Primitive: int32_t + Parent (offset: 4) +[2] Class: ClassC (size: 4, align: 16) + Member: c (offset: 0) + Primitive: int32_t + Member: a (offset: 8) + Primitive: int32_t +)", + R"( [0] Class: ClassA (size: 12) Member: b (offset: 0, align: 8) Primitive: int32_t @@ -600,7 +616,7 @@ TEST(FlattenerTest, Children) { classB.members.push_back(Member(&myint, "b", 0)); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 4)); + classA.parents.push_back(Parent(&classC, 4 * 8)); classB.children.push_back(classA); classC.children.push_back(classA); @@ -635,11 +651,11 @@ TEST(FlattenerTest, ChildrenTwoDeep) { classC.members.push_back(Member(&myint, "c", 0)); classB.parents.push_back(Parent(&classD, 0)); - classB.members.push_back(Member(&myint, "b", 4)); + classB.members.push_back(Member(&myint, "b", 4 * 8)); classA.parents.push_back(Parent(&classB, 0)); - classA.parents.push_back(Parent(&classC, 8)); - classA.members.push_back(Member(&myint, "a", 12)); + classA.parents.push_back(Parent(&classC, 8 * 8)); + classA.members.push_back(Member(&myint, "a", 12 * 8)); classD.children.push_back(classB); classB.children.push_back(classA); @@ -676,7 +692,7 @@ TEST(FlattenerTest, ParentContainer) { auto classA = Class{Class::Kind::Class, "ClassA", 32}; classA.parents.push_back(Parent{&vector, 0}); - classA.members.push_back(Member{&myint, "a", 24}); + classA.members.push_back(Member{&myint, "a", 24 * 8}); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 32) @@ -706,7 +722,7 @@ TEST(FlattenerTest, ParentTwoContainers) { auto classA = Class{Class::Kind::Class, "ClassA", 48}; classA.parents.push_back(Parent{&vector, 0}); - classA.parents.push_back(Parent{&vector, 24}); + classA.parents.push_back(Parent{&vector, 24 * 8}); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 48) @@ -739,7 +755,7 @@ TEST(FlattenerTest, ParentClassAndContainer) { auto classA = Class{Class::Kind::Class, "ClassA", 32}; classA.parents.push_back(Parent{&classB, 0}); - classA.parents.push_back(Parent{&vector, 8}); + classA.parents.push_back(Parent{&vector, 8 * 8}); test(Flattener::createPass(), {classA}, R"( [0] Class: ClassA (size: 32) diff --git a/test/test_remove_ignored.cpp b/test/test_remove_ignored.cpp index 1d9b382..c9c231d 100644 --- a/test/test_remove_ignored.cpp +++ b/test/test_remove_ignored.cpp @@ -11,8 +11,8 @@ TEST(RemoveIgnoredTest, Match) { auto classA = Class{Class::Kind::Class, "ClassA", 12}; classA.members.push_back(Member(&classB, "a", 0)); - classA.members.push_back(Member(&classB, "b", 4)); - classA.members.push_back(Member(&classB, "c", 8)); + classA.members.push_back(Member(&classB, "b", 4 * 8)); + classA.members.push_back(Member(&classB, "c", 8 * 8)); const std::vector>& membersToIgnore = { {"ClassA", "b"}, @@ -44,8 +44,8 @@ TEST(RemoveIgnoredTest, TypeMatchMemberMiss) { auto classA = Class{Class::Kind::Class, "ClassA", 12}; classA.members.push_back(Member(&classB, "a", 0)); - classA.members.push_back(Member(&classB, "b", 4)); - classA.members.push_back(Member(&classB, "c", 8)); + classA.members.push_back(Member(&classB, "b", 4 * 8)); + classA.members.push_back(Member(&classB, "c", 8 * 8)); const std::vector>& membersToIgnore = { {"ClassA", "x"}, @@ -67,8 +67,8 @@ TEST(RemoveIgnoredTest, MemberMatchWrongType) { auto classA = Class{Class::Kind::Class, "ClassA", 12}; classA.members.push_back(Member(&classB, "a", 0)); - classA.members.push_back(Member(&classB, "b", 4)); - classA.members.push_back(Member(&classB, "c", 8)); + classA.members.push_back(Member(&classB, "b", 4 * 8)); + classA.members.push_back(Member(&classB, "c", 8 * 8)); const std::vector>& membersToIgnore = { {"ClassB", "b"},