Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(userspace/libsinsp): expose threadinfo cgroups in plugins table api #2107

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion test/libsinsp_e2e/threadinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ static void run_test(test_type ttype,
case TEST_CGROUPS:
size_t pos = val.find("=");
ASSERT_NE(pos, std::string::npos);
ti.cgroups().push_back(make_pair(val.substr(0, pos), val.substr(pos + 1)));
auto cgroups = ti.cgroups();
cgroups.emplace_back(val.substr(0, pos), val.substr(pos + 1));
ti.set_cgroups(cgroups);
break;
}
}
Expand Down
86 changes: 86 additions & 0 deletions userspace/libsinsp/state/table_adapters.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,92 @@
}
};

/**
* @brief An adapter for the libsinsp::state::table_entry interface
* that wraps a non-owning pointer of arbitrary pair of type T. The underlying pointer
* can be set and unset arbitrarily, making this wrapper suitable for optimized
* allocations. Instances of table_entry from this adapter have no static fields,
* and make the wrapped value available as a single dynamic field. The dynamic
* fields definitions of this wrapper are fixed and immutable.
*/
template<typename Tfirst, typename Tsecond>
class pair_table_entry_adapter : public libsinsp::state::table_entry {
public:
// note: this dynamic definitions are fixed in size and structure,
// so there's no need of worrying about specific identifier checks
// as they should be safely interchangeable
static const constexpr uintptr_t s_dynamic_fields_id = 4321;

struct dynamic_fields_t : public fixed_dynamic_fields_infos {
using _dfi = dynamic_struct::field_info;

inline dynamic_fields_t():
fixed_dynamic_fields_infos(
jasondellaluce marked this conversation as resolved.
Show resolved Hide resolved
{_dfi::build<Tfirst>("pair.first", 0, s_dynamic_fields_id),
_dfi::build<Tsecond>("pair.second", 1, s_dynamic_fields_id)}) {}

virtual ~dynamic_fields_t() = default;
};

inline explicit pair_table_entry_adapter(): table_entry(nullptr), m_value(nullptr) {}

virtual ~pair_table_entry_adapter() = default;

inline std::pair<Tfirst, Tsecond>* value() { return m_value; }
inline const std::pair<Tfirst, Tsecond>* value() const { return m_value; }
inline void set_value(std::pair<Tfirst, Tsecond>* v) { m_value = v; }

protected:
virtual void get_dynamic_field(const dynamic_struct::field_info& i, void* out) override final {
if(i.index() > 1 || i.defs_id() != s_dynamic_fields_id) {
throw sinsp_exception(
"invalid field info passed to pair_table_entry_adapter::get_dynamic_field");
}
if(i.index() == 0) {
return get_dynamic_field(i, &m_value->first, out);
}
return get_dynamic_field(i, &m_value->second, out);
}

virtual void set_dynamic_field(const dynamic_struct::field_info& i,
const void* in) override final {
if(i.index() > 1 || i.defs_id() != s_dynamic_fields_id) {
throw sinsp_exception(
"invalid field info passed to pair_table_entry_adapter::set_dynamic_field");
}

if(i.index() == 0) {
return set_dynamic_field(i, &m_value->first, in);
}
return set_dynamic_field(i, &m_value->second, in);
}

virtual void destroy_dynamic_fields() override final {

Check warning on line 110 in userspace/libsinsp/state/table_adapters.h

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/state/table_adapters.h#L110

Added line #L110 was not covered by tests
// nothing to do
}

private:
std::pair<Tfirst, Tsecond>* m_value;

template<typename T>
inline void get_dynamic_field(const dynamic_struct::field_info& i, const T* value, void* out) {
if(i.info().index() == typeinfo::index_t::TI_STRING) {
*((const char**)out) = ((const std::string*)value)->c_str();
} else {
memcpy(out, (const void*)value, i.info().size());

Check warning on line 122 in userspace/libsinsp/state/table_adapters.h

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/state/table_adapters.h#L122

Added line #L122 was not covered by tests
}
}

template<typename T>
inline void set_dynamic_field(const dynamic_struct::field_info& i, T* value, const void* in) {
if(i.info().index() == typeinfo::index_t::TI_STRING) {
*((std::string*)value) = *((const char**)in);
} else {
memcpy((void*)value, in, i.info().size());

Check warning on line 131 in userspace/libsinsp/state/table_adapters.h

View check run for this annotation

Codecov / codecov/patch

userspace/libsinsp/state/table_adapters.h#L131

Added line #L131 was not covered by tests
}
}
};

/**
* @brief An adapter for the libsinsp::state::table_entry interface
* that wraps a non-owning pointer of arbitrary type T. The underlying pointer
Expand Down
104 changes: 104 additions & 0 deletions userspace/libsinsp/test/plugins.ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,110 @@ TEST_F(sinsp_with_test_input, plugin_subtables_array) {
ASSERT_EQ(subtable->entries_count(), 0);
}

TEST_F(sinsp_with_test_input, plugin_subtables_array_pair) {
const constexpr auto num_entries_from_plugin = 10;

auto& reg = m_inspector.get_table_registry();

register_plugin(&m_inspector, get_plugin_api_sample_syscall_subtables_array_pair);

auto table = reg->get_table<int64_t>("threads");
ASSERT_NE(table, nullptr);
ASSERT_EQ(table->name(), "threads");
ASSERT_EQ(table->entries_count(), 0);
ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of<int64_t>());
ASSERT_EQ(table->dynamic_fields()->fields().size(), 0);

// Test "cgroups" field
auto field = table->static_fields()->find("cgroups");
ASSERT_NE(field, table->static_fields()->end());
ASSERT_EQ(field->second.readonly(), true);
ASSERT_EQ(field->second.valid(), true);
ASSERT_EQ(field->second.name(), "cgroups");
ASSERT_EQ(field->second.info(), libsinsp::state::typeinfo::of<libsinsp::state::base_table*>());

ASSERT_EQ(table->entries_count(), 0);

// add a new entry to the thread table
ASSERT_NE(table->add_entry(5, table->new_entry()), nullptr);
auto entry = table->get_entry(5);
ASSERT_NE(entry, nullptr);
ASSERT_EQ(table->entries_count(), 1);

// obtain a pointer to the subtable (check typing too)
auto subtable_acc = field->second.new_accessor<libsinsp::state::base_table*>();
auto subtable = dynamic_cast<libsinsp::state::stl_container_table_adapter<
std::vector<std::pair<std::string, std::string>>,
libsinsp::state::pair_table_entry_adapter<std::string, std::string>>*>(
entry->get_static_field(subtable_acc));
ASSERT_NE(subtable, nullptr);
ASSERT_EQ(subtable->name(), "cgroups");
ASSERT_EQ(subtable->entries_count(), 0);
// get an accessor to a dynamic field representing the array's values
ASSERT_EQ(subtable->dynamic_fields()->fields().size(), 2); // pair.first, pair.second

auto dfield_first = subtable->dynamic_fields()->fields().find("pair.first");
ASSERT_NE(dfield_first, subtable->dynamic_fields()->fields().end());
ASSERT_EQ(dfield_first->second.readonly(), false);
ASSERT_EQ(dfield_first->second.valid(), true);
ASSERT_EQ(dfield_first->second.name(), "pair.first");
ASSERT_EQ(dfield_first->second.info(), libsinsp::state::typeinfo::of<std::string>());
auto dfield_first_acc = dfield_first->second.new_accessor<std::string>();

auto dfield_second = subtable->dynamic_fields()->fields().find("pair.second");
ASSERT_NE(dfield_second, subtable->dynamic_fields()->fields().end());
ASSERT_EQ(dfield_second->second.readonly(), false);
ASSERT_EQ(dfield_second->second.valid(), true);
ASSERT_EQ(dfield_second->second.name(), "pair.second");
ASSERT_EQ(dfield_second->second.info(), libsinsp::state::typeinfo::of<std::string>());
auto dfield_second_acc = dfield_second->second.new_accessor<std::string>();

// start the event capture
// we coordinate with the plugin by sending open events: for each one received,
// the plugin will take a subsequent action on which we then assert the status
open_inspector();

// step #0: the plugin should populate the fdtable
add_event_advance_ts(increasing_ts(),
0,
PPME_SYSCALL_OPEN_E,
3,
"/tmp/the_file",
PPM_O_RDWR,
0);
ASSERT_EQ(subtable->entries_count(), num_entries_from_plugin);

auto itt = [&](libsinsp::state::table_entry& e) -> bool {
std::string first, second;
e.get_dynamic_field(dfield_first_acc, first);
e.get_dynamic_field(dfield_second_acc, second);
EXPECT_EQ(first, "hello");
EXPECT_EQ(second, "world");
return true;
};
ASSERT_TRUE(subtable->foreach_entry(itt));

// step #1: the plugin should remove one entry from the fdtable
add_event_advance_ts(increasing_ts(),
0,
PPME_SYSCALL_OPEN_E,
3,
"/tmp/the_file",
PPM_O_RDWR,
0);
ASSERT_EQ(subtable->entries_count(), num_entries_from_plugin - 1);

// step #2: the plugin should cleae the fdtable
add_event_advance_ts(increasing_ts(),
0,
PPME_SYSCALL_OPEN_E,
3,
"/tmp/the_file",
PPM_O_RDWR,
0);
ASSERT_EQ(subtable->entries_count(), 0);
}

// Scenario: we load a plugin expecting it to log
// when it's initialized and destroyed.
// We use a callback attached to the logger to assert the message.
Expand Down
Loading
Loading