Skip to content

Commit

Permalink
C++ inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
ttreyer committed May 22, 2024
1 parent de44b04 commit 1bb70eb
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 35 deletions.
37 changes: 26 additions & 11 deletions src/dwarf_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,22 +221,23 @@ SizedType Dwarf::get_stype(const std::string &type_name)
return CreateNone();
}

void Dwarf::resolve_fields(const SizedType &type)
void Dwarf::resolve_fields(std::shared_ptr<Struct> str, lldb::SBType type)
{
if (!type.IsRecordTy())
if (!type.IsValid())
return;

auto type_name = type.GetName();
auto str = bpftrace_->structs.Lookup(type_name).lock();
if (str->HasFields())
return;
for (uint32_t i = 0; i < type.GetNumberOfVirtualBaseClasses(); i++) {
auto parent = type.GetVirtualBaseClassAtIndex(i);
resolve_fields(str, parent.GetType());
}

auto type_dbg = target_.FindFirstType(type_name.c_str());
if (!type_dbg)
return;
for (uint32_t i = 0; i < type.GetNumberOfDirectBaseClasses(); i++) {
auto parent = type.GetDirectBaseClassAtIndex(i);
resolve_fields(str, parent.GetType());
}

for (uint32_t i = 0; i < type_dbg.GetNumberOfFields(); i++) {
auto field = type_dbg.GetFieldAtIndex(i);
for (uint32_t i = 0; i < type.GetNumberOfFields(); i++) {
auto field = type.GetFieldAtIndex(i);
auto field_type = get_stype(field.GetType());
str->AddField(field.GetName() ?: "",
get_stype(field.GetType()),
Expand All @@ -246,6 +247,20 @@ void Dwarf::resolve_fields(const SizedType &type)
}
}

void Dwarf::resolve_fields(const SizedType &type)
{
if (!type.IsRecordTy())
return;

auto type_name = type.GetName();
auto str = bpftrace_->structs.Lookup(type_name).lock();
if (str->HasFields())
return;

auto type_dbg = target_.FindFirstType(type_name.c_str());
resolve_fields(str, type_dbg);
}

std::optional<Bitfield> Dwarf::resolve_bitfield(lldb::SBTypeMember field)
{
if (!field.IsBitfield())
Expand Down
1 change: 1 addition & 0 deletions src/dwarf_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Dwarf {

std::string get_type_name(lldb::SBType type);
SizedType get_stype(lldb::SBType type, bool resolve_structs = true);
void resolve_fields(std::shared_ptr<Struct> str, lldb::SBType type);
std::optional<Bitfield> resolve_bitfield(lldb::SBTypeMember field);

BPFtrace *bpftrace_;
Expand Down
2 changes: 1 addition & 1 deletion src/struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void Struct::AddField(const std::string &field_name,
bool is_data_loc)
{
if (!HasField(field_name))
fields.emplace_back(Field{ .name = field_name,
fields.push_back(Field{ .name = field_name,
.type = type,
.offset = offset,
.bitfield = bitfield,
Expand Down
16 changes: 16 additions & 0 deletions tests/data/data_source_cxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,31 @@ class Child : public Parent {
Child(int a, int b, int c, int d, int e, int f) : Parent(a, b, c, d), d(d + 1), e(e), f(f) {}
};

class LittleChild : public Child {
public:
int g;

LittleChild(int a, int b, int c, int d, int e, int f, int g) : Child(a, b, c, d, e, f), g(g) {}
};

int func_1(Child &c, Parent &p __attribute__((unused)))
{
return dynamic_cast<Parent&>(c).d;
}

int func_2(LittleChild &lc)
{
return dynamic_cast<Parent&>(lc).d;
}

int main(void)
{
Parent p{1, 2, 3, 4};
Child c{1, 2, 3, 4, 5, 6};
func_1(c, p);

LittleChild lc{1, 2, 3, 4, 5, 6, 7};
func_2(lc);

return 0;
}
123 changes: 100 additions & 23 deletions tests/field_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,38 +478,115 @@ TEST_F(field_analyser_dwarf, parse_arrays)
EXPECT_EQ(arrs->GetField("flexible").offset, 64);
}

static void CheckParentFields(const std::shared_ptr<Struct> &cls, bool is_d_shadowed = false)
{
EXPECT_TRUE(cls->HasField("a"));
EXPECT_TRUE(cls->GetField("a").type.IsIntTy());
EXPECT_EQ(cls->GetField("a").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("a").offset, 0);

EXPECT_TRUE(cls->HasField("b"));
EXPECT_TRUE(cls->GetField("b").type.IsIntTy());
EXPECT_EQ(cls->GetField("b").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("b").offset, 4);

EXPECT_TRUE(cls->HasField("c"));
EXPECT_TRUE(cls->GetField("c").type.IsIntTy());
EXPECT_EQ(cls->GetField("c").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("c").offset, 8);

// The Child class also has a field 'd', which shadows the Parent's.
if (is_d_shadowed == false) {
EXPECT_TRUE(cls->HasField("d"));
EXPECT_TRUE(cls->GetField("d").type.IsIntTy());
EXPECT_EQ(cls->GetField("d").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("d").offset, 12);
}
}

static void CheckChildFields(const std::shared_ptr<Struct> &cls)
{
CheckParentFields(cls, true);

// Parent's field 'd' is shadowed by Child's field 'd'
EXPECT_TRUE(cls->HasField("d"));
EXPECT_TRUE(cls->GetField("d").type.IsIntTy());
EXPECT_EQ(cls->GetField("d").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("d").offset, 16);

EXPECT_TRUE(cls->HasField("e"));
EXPECT_TRUE(cls->GetField("e").type.IsIntTy());
EXPECT_EQ(cls->GetField("e").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("e").offset, 20);

EXPECT_TRUE(cls->HasField("f"));
EXPECT_TRUE(cls->GetField("f").type.IsIntTy());
EXPECT_EQ(cls->GetField("f").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("f").offset, 24);
}

static void CheckLittleChildFields(const std::shared_ptr<Struct> &cls)
{
CheckChildFields(cls);

EXPECT_TRUE(cls->HasField("g"));
EXPECT_TRUE(cls->GetField("g").type.IsIntTy());
EXPECT_EQ(cls->GetField("g").type.GetSize(), 4);
EXPECT_EQ(cls->GetField("g").offset, 28);
}

TEST_F(field_analyser_dwarf, parse_class)
{
BPFtrace bpftrace;
std::string uprobe = "uprobe:" + std::string(cxx_bin_);
test(bpftrace, uprobe + ":_Z6func_1R5ChildR6Parent { $x = args.p->a; }", 0);
test(bpftrace, uprobe + ":cpp:func_1 { $x = args.p->a; }", 0);

ASSERT_TRUE(bpftrace.structs.Has("Parent"));
auto cls = bpftrace.structs.Lookup("Parent").lock();

ASSERT_TRUE(cls->HasFields());
ASSERT_EQ(cls->fields.size(), 4);
ASSERT_EQ(cls->size, 16);

ASSERT_TRUE(cls->HasField("a"));
ASSERT_TRUE(cls->GetField("a").type.IsIntTy());
ASSERT_EQ(cls->GetField("a").type.GetSize(), 4);
ASSERT_EQ(cls->GetField("a").offset, 0);

ASSERT_TRUE(cls->HasField("b"));
ASSERT_TRUE(cls->GetField("b").type.IsIntTy());
ASSERT_EQ(cls->GetField("b").type.GetSize(), 4);
ASSERT_EQ(cls->GetField("b").offset, 4);

ASSERT_TRUE(cls->HasField("c"));
ASSERT_TRUE(cls->GetField("c").type.IsIntTy());
ASSERT_EQ(cls->GetField("c").type.GetSize(), 4);
ASSERT_EQ(cls->GetField("c").offset, 8);

ASSERT_TRUE(cls->HasField("d"));
ASSERT_TRUE(cls->GetField("d").type.IsIntTy());
ASSERT_EQ(cls->GetField("d").type.GetSize(), 4);
ASSERT_EQ(cls->GetField("d").offset, 12);
EXPECT_EQ(cls->fields.size(), 4);
EXPECT_EQ(cls->size, 16);

CheckParentFields(cls);
}

TEST_F(field_analyser_dwarf, parse_inheritance)
{
BPFtrace bpftrace;
std::string uprobe = "uprobe:" + std::string(cxx_bin_);
test(bpftrace, uprobe + ":cpp:func_1 { $x = args.c->a; }", 0);

ASSERT_TRUE(bpftrace.structs.Has("Child"));
auto cls = bpftrace.structs.Lookup("Child").lock();

ASSERT_TRUE(cls->HasFields());
// The child has a total of 7 fields with its parent's,
// but field 'd' is shadowed by Child's field 'd'
// so only 6 are shown in the Child's struct
ASSERT_EQ(cls->fields.size(), (4 - 1) + 3);
ASSERT_EQ(cls->size, 16 + 12);

CheckChildFields(cls);
}

TEST_F(field_analyser_dwarf, parse_inheritance_chain)
{
BPFtrace bpftrace;
std::string uprobe = "uprobe:" + std::string(cxx_bin_);
test(bpftrace, uprobe + ":cpp:func_2 { $x = args.lc->a; }", 0);

ASSERT_TRUE(bpftrace.structs.Has("LittleChild"));
auto cls = bpftrace.structs.Lookup("LittleChild").lock();

ASSERT_TRUE(cls->HasFields());
// The child has a total of 7 fields with its parent's,
// but field 'd' is shadowed by Child's field 'd'
// so only 6 are showned in the Child's struct
ASSERT_EQ(cls->fields.size(), (4 - 1) + 3 + 1);
ASSERT_EQ(cls->size, 16 + 12 + 4);

CheckLittleChildFields(cls);
}

TEST_F(field_analyser_dwarf, parse_struct_anonymous_fields)
Expand Down

0 comments on commit 1bb70eb

Please sign in to comment.