Skip to content

Commit

Permalink
C++ Multiple Inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
ttreyer committed May 22, 2024
1 parent 1bb70eb commit a1ce0a6
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 74 deletions.
58 changes: 32 additions & 26 deletions src/dwarf_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <lldb/API/LLDB.h>
#include <llvm/Config/llvm-config.h>
#include <memory>
#include <queue>
#include <string>

namespace bpftrace {
Expand Down Expand Up @@ -221,31 +222,10 @@ SizedType Dwarf::get_stype(const std::string &type_name)
return CreateNone();
}

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

for (uint32_t i = 0; i < type.GetNumberOfVirtualBaseClasses(); i++) {
auto parent = type.GetVirtualBaseClassAtIndex(i);
resolve_fields(str, parent.GetType());
}

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.GetNumberOfFields(); i++) {
auto field = type.GetFieldAtIndex(i);
auto field_type = get_stype(field.GetType());
str->AddField(field.GetName() ?: "",
get_stype(field.GetType()),
field.GetOffsetInBytes(),
resolve_bitfield(field),
false);
}
}
struct Subobject {
lldb::SBType type;
size_t offset;
};

void Dwarf::resolve_fields(const SizedType &type)
{
Expand All @@ -258,7 +238,33 @@ void Dwarf::resolve_fields(const SizedType &type)
return;

auto type_dbg = target_.FindFirstType(type_name.c_str());
resolve_fields(str, type_dbg);
if (!type_dbg.IsValid())
return;

std::queue<Subobject> subobjects{ std::deque{ Subobject{ type_dbg, 0 } } };
while (!subobjects.empty()) {
auto &subobject = subobjects.front();

// Collect the fields into str, duplicates will be ignored.
for (uint32_t i = 0; i < subobject.type.GetNumberOfFields(); i++) {
auto field = subobject.type.GetFieldAtIndex(i);
str->AddField(field.GetName() ?: "",
get_stype(field.GetType()),
subobject.offset + field.GetOffsetInBytes(),
resolve_bitfield(field),
false);
}

// Queue the bases for further processing.
for (uint32_t i = 0; i < subobject.type.GetNumberOfDirectBaseClasses();
i++) {
auto base = subobject.type.GetDirectBaseClassAtIndex(i);
subobjects.push(Subobject{ base.GetType(),
subobject.offset + base.GetOffsetInBytes() });
}

subobjects.pop();
}
}

std::optional<Bitfield> Dwarf::resolve_bitfield(lldb::SBTypeMember field)
Expand Down
1 change: 0 additions & 1 deletion src/dwarf_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ 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
8 changes: 4 additions & 4 deletions src/struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ void Struct::AddField(const std::string &field_name,
{
if (!HasField(field_name))
fields.push_back(Field{ .name = field_name,
.type = type,
.offset = offset,
.bitfield = bitfield,
.is_data_loc = is_data_loc });
.type = type,
.offset = offset,
.bitfield = bitfield,
.is_data_loc = is_data_loc });
}

bool Struct::HasFields() const
Expand Down
4 changes: 2 additions & 2 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ not known until test time. The following runtime variables are available for the

### Test programs

You can add test programs for your runtime tests by placing a `.c` file corresponding to your test program in `tests/testprogs`.
You can add test programs for your runtime tests by placing a `.c` or `.cpp` file corresponding to your test program in `tests/testprogs`.

You can add test libraries for your runtime tests by placing a `.c` file corresponding to your test library in `tests/testlibs`.
You can add test libraries for your runtime tests by placing a `.c` or `.cpp` file corresponding to your test library in `tests/testlibs`.

The test file `tests/testprogs/my_test.c` will result in an executable that you can call and probe in your runtime test at `./testprogs/my_test`

Expand Down
2 changes: 2 additions & 0 deletions tests/data/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,7 @@ add_custom_command(

add_custom_target(debuginfo_btf_data DEPENDS ${BTF_DATA_H})

# BTF doesn't support C++, so we only generate a data_source_cxx executable
# to run the semantic_analyser tests on.
add_executable(data_source_cxx data_source_cxx.cpp)
target_compile_options(data_source_cxx PRIVATE ${DATA_SOURCE_CFLAGS})
98 changes: 77 additions & 21 deletions tests/data/data_source_cxx.cpp
Original file line number Diff line number Diff line change
@@ -1,49 +1,105 @@
class Parent {
private:
int a;
protected:
int b;
public:
int c;
int d; // Shadowed by Child::d, but should be reachable with a cast
private:
int a;

Parent(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) {}
protected:
int b;

public:
int c;
int d; // Shadowed by Child::d, but should be reachable with a cast

Parent(int a, int b, int c, int d) : a(a), b(b), c(c), d(d)
{
}
};

class Child : public Parent {
public:
int d;
int e;
int f;
public:
int d;
int e;
int f;

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) {}
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;
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) {}
LittleChild(int a, int b, int c, int d, int e, int f, int g)
: Child(a, b, c, d, e, f), g(g)
{
}
};

struct Top {
int x;
};

struct Left : public Top {
int y;
};

struct Right : public Top {
int z;
};

struct Bottom : public Left, public Right {
int w;
};

struct Multi : public Parent, public Top {
int abc;

Multi(int a, int b, int c, int d, int e)
: Parent{ a, b, c, d }, Top{ e }, abc{ e + 1 }
{
}
};

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

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

int func_3(Multi &m, Bottom &b __attribute__((unused)))
{
return m.abc;
}

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

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
func_1
has no external side effects).

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

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
func_2
has no external side effects).

Multi m{ 1, 2, 3, 4, 5 };
Bottom b{
{
// Left
{ 1 }, // Left's Top
2 // Left's y
},
{
// Right
{ 3 }, // Right's Top
4 // Right's z
},
5 // Bottom's w
};
func_3(m, b);

Check warning

Code scanning / CodeQL

Expression has no effect Warning

This expression has no effect (because
func_3
has no external side effects).

return 0;
}
29 changes: 28 additions & 1 deletion tests/field_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,8 @@ 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)
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());
Expand Down Expand Up @@ -589,6 +590,32 @@ TEST_F(field_analyser_dwarf, parse_inheritance_chain)
CheckLittleChildFields(cls);
}

TEST_F(field_analyser_dwarf, parse_inheritance_multi)
{
BPFtrace bpftrace;
std::string uprobe = "uprobe:" + std::string(cxx_bin_);
test(bpftrace, uprobe + ":cpp:func_3 { $x = args.m->abc; }", 0);

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

ASSERT_TRUE(cls->HasFields());
ASSERT_EQ(cls->fields.size(), 6);
ASSERT_EQ(cls->size, 24);

CheckParentFields(cls);

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

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

TEST_F(field_analyser_dwarf, parse_struct_anonymous_fields)
{
GTEST_SKIP() << "Anonymous fields not supported #3084";
Expand Down
2 changes: 1 addition & 1 deletion tests/testlibs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
file(GLOB testlib_sources CONFIGURE_DEPENDS *.c)
file(GLOB testlib_sources CONFIGURE_DEPENDS *.c *.cpp)
set(testlibtargets "")
foreach(testlib_source ${testlib_sources})
get_filename_component(testlib_name ${testlib_source} NAME_WE)
Expand Down
43 changes: 25 additions & 18 deletions tests/testprogs/uprobe_test_cxx.cpp
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
#include <unistd.h>

class Parent {
private:
int a;
protected:
int b;
public:
int c;
int d; // Shadowed by Child::d, but should be reachable with a cast

Parent(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) {}
private:
int a;

protected:
int b;

public:
int c;
int d; // Shadowed by Child::d, but should be reachable with a cast

Parent(int a, int b, int c, int d) : a(a), b(b), c(c), d(d)
{
}
};

class Child : public Parent {
public:
int d;
int e;
int f;

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) {}
public:
int d;
int e;
int f;

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)
{
}
};

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

int uprobeArray(int (&array)[10])
Expand All @@ -35,8 +42,8 @@ int main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
{
// usleep(1000000);

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

Parent p{1, 2, 3, 4};
Child c{1, 2, 3, 4, 5, 6};
Parent p{ 1, 2, 3, 4 };
Child c{ 1, 2, 3, 4, 5, 6 };

return 0;
}

0 comments on commit a1ce0a6

Please sign in to comment.