mirror of
https://github.com/JakeHillion/object-introspection.git
synced 2024-11-09 21:24:14 +00:00
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
This commit is contained in:
parent
c9bcf5e760
commit
e86ebb7aff
@ -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,6 +413,7 @@ void CodeGen::getClassSizeFuncConcrete(std::string_view funcName,
|
||||
}
|
||||
|
||||
code += " JLOG(\"" + member.name + " @\");\n";
|
||||
if (member.bitsize == 0)
|
||||
code += " JLOGPTR(&t." + member.name + ");\n";
|
||||
code += " getSizeType(t." + member.name + ", returnArg);\n";
|
||||
}
|
||||
|
@ -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<Member>& 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;
|
||||
|
||||
if (paddingBits % 8 == 0) {
|
||||
// Pad with an array of bytes
|
||||
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int8);
|
||||
auto* paddingArray = typeGraph_.make_type<Array>(primitive, padding);
|
||||
paddedMembers.emplace_back(paddingArray, MemberPrefix, prevMemberEnd);
|
||||
auto* paddingArray =
|
||||
typeGraph_.make_type<Array>(primitive, paddingBits / 8);
|
||||
paddedMembers.emplace_back(paddingArray, MemberPrefix, prevMemberEndBits);
|
||||
} else {
|
||||
// Pad with a bitfield
|
||||
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int64);
|
||||
paddedMembers.emplace_back(primitive, MemberPrefix, prevMemberEndBits,
|
||||
paddingBits);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace type_graph
|
||||
|
@ -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<Type*> visited_;
|
||||
|
@ -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,
|
||||
|
@ -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<Container*>(&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++];
|
||||
|
@ -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<double>(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<double>(member.bitOffset) / 8;
|
||||
out_ << align_str(member.align);
|
||||
if (member.bitsize != 0) {
|
||||
out_ << ", bitsize: " << member.bitsize;
|
||||
}
|
||||
out_ << ")" << std::endl;
|
||||
print(*member.type);
|
||||
depth_--;
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ void RemoveIgnored::visit(Class& c) {
|
||||
auto* primitive = typeGraph_.make_type<Primitive>(Primitive::Kind::Int8);
|
||||
auto* paddingArray =
|
||||
typeGraph_.make_type<Array>(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};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
125
test/integration/bitfields.toml
Normal file
125
test/integration/bitfields.toml
Normal file
@ -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}
|
||||
]}]'''
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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<std::pair<std::string, std::string>>& 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<std::pair<std::string, std::string>>& 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<std::pair<std::string, std::string>>& membersToIgnore = {
|
||||
{"ClassB", "b"},
|
||||
|
Loading…
Reference in New Issue
Block a user