diff --git a/userspace/libsinsp/CMakeLists.txt b/userspace/libsinsp/CMakeLists.txt index ccfcce0ef8..1b46388028 100644 --- a/userspace/libsinsp/CMakeLists.txt +++ b/userspace/libsinsp/CMakeLists.txt @@ -108,6 +108,7 @@ add_library( sinsp_suppress.cpp events/sinsp_events.cpp events/sinsp_events_ppm_sc.cpp + state/table.cpp ) if(NOT WIN32 diff --git a/userspace/libsinsp/fdinfo.cpp b/userspace/libsinsp/fdinfo.cpp index f04d530405..7df3fd3e58 100644 --- a/userspace/libsinsp/fdinfo.cpp +++ b/userspace/libsinsp/fdinfo.cpp @@ -292,7 +292,7 @@ static const auto s_fdtable_static_fields = sinsp_fdinfo().static_fields(); // sinsp_fdtable implementation /////////////////////////////////////////////////////////////////////////////// sinsp_fdtable::sinsp_fdtable(sinsp* inspector): - table("file_descriptors", &s_fdtable_static_fields) { + built_in_table("file_descriptors", &s_fdtable_static_fields) { m_tid = 0; m_inspector = inspector; if(m_inspector != nullptr) { diff --git a/userspace/libsinsp/fdinfo.h b/userspace/libsinsp/fdinfo.h index 47d1ac4b78..a6dc168fae 100644 --- a/userspace/libsinsp/fdinfo.h +++ b/userspace/libsinsp/fdinfo.h @@ -373,7 +373,7 @@ struct sinsp_stats_v2; /////////////////////////////////////////////////////////////////////////////// // fd info table /////////////////////////////////////////////////////////////////////////////// -class sinsp_fdtable : public libsinsp::state::table { +class sinsp_fdtable : public libsinsp::state::built_in_table { public: typedef std::function fdtable_visitor_t; diff --git a/userspace/libsinsp/plugin.h b/userspace/libsinsp/plugin.h index f65468d682..3854287cc1 100644 --- a/userspace/libsinsp/plugin.h +++ b/userspace/libsinsp/plugin.h @@ -38,10 +38,13 @@ limitations under the License. #include #endif +namespace libsinsp::state { +class base_table; +} /** * @brief An object-oriented representation of a plugin. */ -class sinsp_plugin { +class sinsp_plugin : public libsinsp::state::sinsp_table_owner { public: struct open_param { open_param() = default; @@ -113,7 +116,6 @@ class sinsp_plugin { m_inited(false), m_state(nullptr), m_handle(handle), - m_last_owner_err(), m_scap_source_plugin(), m_fields_info(), m_fields(), @@ -128,11 +130,6 @@ class sinsp_plugin { m_table_infos(), m_owned_tables(), m_accessed_tables(), - m_accessed_entries(), - m_accessed_table_fields(), - m_ephemeral_tables(), - m_ephemeral_tables_clear(false), - m_accessed_entries_clear(false), m_thread_pool(tpool) {} virtual ~sinsp_plugin(); sinsp_plugin(const sinsp_plugin& s) = delete; @@ -232,7 +229,6 @@ class sinsp_plugin { bool m_inited; ss_plugin_t* m_state; plugin_handle_t* m_handle; - std::string m_last_owner_err; /** Event Sourcing **/ scap_source_plugin m_scap_source_plugin; @@ -276,181 +272,333 @@ class sinsp_plugin { /** Table API state and helpers **/ - // wraps instances of libsinsp::state::XXX_struct::field_accessor and - // help making them comply to the plugin API state tables definitions - struct sinsp_field_accessor_wrapper { - // depending on the value of `dynamic`, one of: - // - libsinsp::state::static_struct::field_accessor - // - libsinsp::state::dynamic_struct::field_accessor - void* accessor = nullptr; - bool dynamic = false; - ss_plugin_state_type data_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; - ss_plugin_state_type subtable_key_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; - - inline sinsp_field_accessor_wrapper() = default; - ~sinsp_field_accessor_wrapper(); - inline sinsp_field_accessor_wrapper(const sinsp_field_accessor_wrapper& s) = delete; - inline sinsp_field_accessor_wrapper& operator=(const sinsp_field_accessor_wrapper& s) = - delete; - inline sinsp_field_accessor_wrapper(sinsp_field_accessor_wrapper&& s); - inline sinsp_field_accessor_wrapper& operator=(sinsp_field_accessor_wrapper&& s); - }; - - // wraps instances of libsinsp::state::table and help making them comply - // to the plugin API state tables definitions - struct sinsp_table_wrapper { - ss_plugin_state_type m_key_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; - sinsp_plugin* m_owner_plugin = nullptr; - libsinsp::state::base_table* m_table = nullptr; - std::vector m_field_list; - std::unordered_map - m_field_accessors; - - // used to optimize cases where this wraps a plugin-defined table directly - const sinsp_plugin* m_table_plugin_owner = nullptr; - ss_plugin_table_input* m_table_plugin_input = nullptr; - - inline sinsp_table_wrapper() = default; - virtual ~sinsp_table_wrapper() = default; - inline sinsp_table_wrapper(const sinsp_table_wrapper& s) = delete; - inline sinsp_table_wrapper& operator=(const sinsp_table_wrapper& s) = delete; - - void unset(); - bool is_set() const; - template - void set(sinsp_plugin* p, libsinsp::state::table* t); - - // static functions, will be used to populate vtable functions where - // ss_plugin_table_t* will be represented by a sinsp_table_wrapper* - static inline const ss_plugin_table_fieldinfo* list_fields(ss_plugin_table_t* _t, - uint32_t* nfields); - static inline ss_plugin_table_field_t* get_field(ss_plugin_table_t* _t, - const char* name, - ss_plugin_state_type data_type); - static inline ss_plugin_table_field_t* add_field(ss_plugin_table_t* _t, - const char* name, - ss_plugin_state_type data_type); - static inline const char* get_name(ss_plugin_table_t* _t); - static inline uint64_t get_size(ss_plugin_table_t* _t); - static inline ss_plugin_table_entry_t* get_entry(ss_plugin_table_t* _t, - const ss_plugin_state_data* key); - static inline ss_plugin_rc read_entry_field(ss_plugin_table_t* _t, - ss_plugin_table_entry_t* _e, - const ss_plugin_table_field_t* f, - ss_plugin_state_data* out); - ; - static inline void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e); - static inline ss_plugin_bool iterate_entries(ss_plugin_table_t* _t, - ss_plugin_table_iterator_func_t it, - ss_plugin_table_iterator_state_t* s); - static inline ss_plugin_rc clear(ss_plugin_table_t* _t); - static inline ss_plugin_rc erase_entry(ss_plugin_table_t* _t, - const ss_plugin_state_data* key); - static inline ss_plugin_table_entry_t* create_table_entry(ss_plugin_table_t* _t); - static inline void destroy_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e); - static inline ss_plugin_table_entry_t* add_entry(ss_plugin_table_t* _t, - const ss_plugin_state_data* key, - ss_plugin_table_entry_t* _e); - static inline ss_plugin_rc write_entry_field(ss_plugin_table_t* _t, - ss_plugin_table_entry_t* e, - const ss_plugin_table_field_t* f, - const ss_plugin_state_data* in); - ; - }; - - // a wrapper around sinsp_table_wrapper (yes...) that makes it comply to the - // ss_plugin_table_input facade, thus being accessible through plugin API - struct sinsp_table_input { - ss_plugin_table_input input; - ss_plugin_table_fields_vtable_ext fields_vtable; - ss_plugin_table_reader_vtable_ext reader_vtable; - ss_plugin_table_writer_vtable_ext writer_vtable; - sinsp_table_wrapper wrapper; - - sinsp_table_input(); - inline ~sinsp_table_input() = default; - inline sinsp_table_input(const sinsp_table_input& s) = delete; - inline sinsp_table_input& operator=(const sinsp_table_input& s) = delete; - - void update(); - }; - std::shared_ptr m_table_registry; std::vector m_table_infos; std::unordered_map> m_owned_tables; /* contains tables that the plugin accessed at least once */ - std::unordered_map m_accessed_tables; - std::list> - m_accessed_entries; // using lists for ptr stability - std::list - m_accessed_table_fields; // note: lists have pointer stability - std::list m_ephemeral_tables; // note: lists have pointer stability - bool m_ephemeral_tables_clear; - bool m_accessed_entries_clear; - - inline void clear_ephemeral_tables() { - if(m_ephemeral_tables_clear) { - // quick break-out that prevents us from looping over the - // whole list in the critical path, in case of no accessed table + std::unordered_map m_accessed_tables; + static void table_field_api(ss_plugin_table_fields_vtable& out, + ss_plugin_table_fields_vtable_ext& extout); + static void table_read_api(ss_plugin_table_reader_vtable& out, + ss_plugin_table_reader_vtable_ext& extout); + static void table_write_api(ss_plugin_table_writer_vtable& out, + ss_plugin_table_writer_vtable_ext& extout); + static ss_plugin_table_info* table_api_list_tables(ss_plugin_owner_t* o, uint32_t* ntables); + static ss_plugin_table_t* table_api_get_table(ss_plugin_owner_t* o, + const char* name, + ss_plugin_state_type key_type); + static ss_plugin_rc table_api_add_table(ss_plugin_owner_t* o, const ss_plugin_table_input* in); + + std::shared_ptr m_thread_pool; +}; + +template +struct span { + T* m_begin; + size_t m_size; + + T* begin() const noexcept { return m_begin; } + + T* end() const noexcept { return m_begin + m_size; } + + auto size() const { return m_size; } +}; + +template +static void wrap_state_data(const KeyType& key, ss_plugin_state_data& out); + +template<> +inline void wrap_state_data(const int8_t& key, ss_plugin_state_data& out) { + out.s8 = key; +} + +template<> +inline void wrap_state_data(const int16_t& key, ss_plugin_state_data& out) { + out.s16 = key; +} + +template<> +inline void wrap_state_data(const int32_t& key, ss_plugin_state_data& out) { + out.s32 = key; +} + +template<> +inline void wrap_state_data(const int64_t& key, ss_plugin_state_data& out) { + out.s64 = key; +} + +template<> +inline void wrap_state_data(const uint8_t& key, ss_plugin_state_data& out) { + out.u8 = key; +} + +template<> +inline void wrap_state_data(const uint16_t& key, ss_plugin_state_data& out) { + out.u16 = key; +} + +template<> +inline void wrap_state_data(const uint32_t& key, ss_plugin_state_data& out) { + out.u32 = key; +} + +template<> +inline void wrap_state_data(const uint64_t& key, ss_plugin_state_data& out) { + out.u64 = key; +} + +template<> +inline void wrap_state_data(const std::string& key, ss_plugin_state_data& out) { + out.str = key.c_str(); +} + +template<> +inline void wrap_state_data(const bool& key, ss_plugin_state_data& out) { + out.b = key; +} + +template<> +inline void wrap_state_data(libsinsp::state::base_table* const& key, + ss_plugin_state_data& out) { + out.table = static_cast(key); +} + +template +static void unwrap_state_data(const ss_plugin_state_data& val, FieldType& out); + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, int8_t& out) { + out = val.s8; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, int16_t& out) { + out = val.s16; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, int32_t& out) { + out = val.s32; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, int64_t& out) { + out = val.s64; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, uint8_t& out) { + out = val.u8; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, uint16_t& out) { + out = val.u16; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, uint32_t& out) { + out = val.u32; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, uint64_t& out) { + out = val.u64; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, std::string& out) { + out = val.str; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, bool& out) { + out = val.b != 0; +} + +template<> +inline void unwrap_state_data(const ss_plugin_state_data& val, + ss_plugin_table_t*& out) { + out = val.table; +} + +class sinsp_table_entry { + enum class entry_dtor { NONE, DESTROY, RELEASE }; + +public: + sinsp_table_entry() = delete; + sinsp_table_entry(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* entry, + libsinsp::state::base_table* table, + entry_dtor dtor): + m_owner(owner), + m_entry(entry), + m_table(table), + m_dtor(dtor) {} + sinsp_table_entry(const sinsp_table_entry& s) = delete; + sinsp_table_entry& operator=(const sinsp_table_entry& s) = delete; + sinsp_table_entry(sinsp_table_entry&& s) = default; + sinsp_table_entry& operator=(sinsp_table_entry&& s) = default; + ~sinsp_table_entry() { + if(m_entry == nullptr) { return; } - for(auto& et : m_ephemeral_tables) { - et.wrapper.unset(); - et.update(); + switch(m_dtor) { + case entry_dtor::NONE: + break; + case entry_dtor::DESTROY: + m_table->destroy_table_entry(m_owner, m_entry); + break; + case entry_dtor::RELEASE: + m_table->release_table_entry(m_owner, m_entry); + break; + default: + ASSERT(false); } - m_ephemeral_tables_clear = true; } - inline sinsp_table_input& find_unset_ephemeral_table() { - m_ephemeral_tables_clear = false; - for(auto& et : m_ephemeral_tables) { - if(!et.wrapper.is_set()) { - return et; - } + template + void read_field(const ss_plugin_table_field_t* field, FieldType& out) { + ss_plugin_state_data data; + auto rc = m_table->read_entry_field(m_owner, m_entry, field, &data); + if(rc != SS_PLUGIN_SUCCESS) { + throw sinsp_exception("failed to read field: " + this->m_owner->m_last_owner_err); } - return m_ephemeral_tables.emplace_back(); + + unwrap_state_data(data, out); } - inline void clear_accessed_entries() { - if(m_accessed_entries_clear) { - // quick break-out that prevents us from looping over the - // whole list in the critical path - return; + template + void write_field(const ss_plugin_table_field_t* field, const FieldType& in) { + ss_plugin_state_data data; + wrap_state_data(in, data); + auto rc = m_table->write_entry_field(m_owner, m_entry, field, &data); + if(rc != SS_PLUGIN_SUCCESS) { + throw sinsp_exception("failed to write field: " + this->m_owner->m_last_owner_err); + } + } + +private: + libsinsp::state::sinsp_table_owner* m_owner; + ss_plugin_table_entry_t* m_entry; + libsinsp::state::base_table* m_table; + entry_dtor m_dtor; + + template + friend class sinsp_table; +}; + +template +class sinsp_table { +public: + sinsp_table(libsinsp::state::sinsp_table_owner* p, libsinsp::state::base_table* t): + m_owner_plugin(p), + m_table(t) { + if(m_table->key_info().type_id() != libsinsp::state::typeinfo::of().type_id()) { + throw sinsp_exception("table key type mismatch, requested='" + + std::string(libsinsp::state::typeinfo::of().name()) + + "', actual='" + std::string(m_table->key_info().name()) + "'"); } - for(auto& et : m_accessed_entries) { - if(et != nullptr) { - // if we get here, it means that the plugin did not - // release some of the entries it acquired - ASSERT(false); - et.reset(); - }; + } + + std::string_view name() const { return m_table->name(); } + + size_t entries_count() const { return m_table->get_size(m_owner_plugin); } + + const libsinsp::state::typeinfo& key_info() const { return m_table->key_info(); } + + span fields() const { + uint32_t nfields; + return {m_table->list_fields(m_owner_plugin, &nfields), nfields}; + } + + const ss_plugin_table_fieldinfo* get_field_info(const char* name) const { + auto fields = this->fields(); + auto field_name = std::string_view(name); + auto field = + std::find_if(fields.begin(), fields.end(), [&](const ss_plugin_table_fieldinfo& f) { + return f.name == field_name; + }); + + return field; + } + + template + ss_plugin_table_field_t* get_field(const char* name) { + auto typeinfo = libsinsp::state::typeinfo::of(); + return m_table->get_field(m_owner_plugin, name, typeinfo.type_id()); + } + + template + ss_plugin_table_field_t* add_field(const char* name) { + auto typeinfo = libsinsp::state::typeinfo::of(); + return m_table->add_field(m_owner_plugin, name, typeinfo.type_id()); + } + + sinsp_table_entry get_entry(const KeyType& key) { + ss_plugin_state_data key_data; + wrap_state_data(key, key_data); + auto entry = m_table->get_entry(m_owner_plugin, &key_data); + if(entry == nullptr) { + throw sinsp_exception("could not get entry: " + m_owner_plugin->m_last_owner_err); } - m_accessed_entries_clear = true; + return sinsp_table_entry(m_owner_plugin, + entry, + m_table, + sinsp_table_entry::entry_dtor::RELEASE); } - inline std::shared_ptr* find_unset_accessed_table_entry() { - m_accessed_entries_clear = false; - for(auto& et : m_accessed_entries) { - if(et == nullptr) { - return &et; - } + sinsp_table_entry new_entry() { + auto entry = m_table->create_table_entry(m_owner_plugin); + if(entry == nullptr) { + throw sinsp_exception("could not create entry: " + m_owner_plugin->m_last_owner_err); } - return &m_accessed_entries.emplace_back(); + return sinsp_table_entry(m_owner_plugin, + entry, + m_table, + sinsp_table_entry::entry_dtor::DESTROY); } - static void table_field_api(ss_plugin_table_fields_vtable& out, - ss_plugin_table_fields_vtable_ext& extout); - static void table_read_api(ss_plugin_table_reader_vtable& out, - ss_plugin_table_reader_vtable_ext& extout); - static void table_write_api(ss_plugin_table_writer_vtable& out, - ss_plugin_table_writer_vtable_ext& extout); - static ss_plugin_table_info* table_api_list_tables(ss_plugin_owner_t* o, uint32_t* ntables); - static ss_plugin_table_t* table_api_get_table(ss_plugin_owner_t* o, - const char* name, - ss_plugin_state_type key_type); - static ss_plugin_rc table_api_add_table(ss_plugin_owner_t* o, const ss_plugin_table_input* in); + void add_entry(const KeyType& key, sinsp_table_entry& entry) { + ss_plugin_state_data key_data; + wrap_state_data(key, key_data); - std::shared_ptr m_thread_pool; + auto table_entry = m_table->add_entry(m_owner_plugin, &key_data, entry.m_entry); + entry.m_entry = table_entry; + entry.m_dtor = sinsp_table_entry::entry_dtor::RELEASE; + } - friend struct sinsp_table_wrapper; + bool foreach_entry(std::function pred) { + struct iter_state { + libsinsp::state::sinsp_table_owner* m_owner_plugin; + libsinsp::state::base_table* m_table; + std::function& pred; + } state = {m_owner_plugin, m_table, pred}; + + return m_table->iterate_entries( + m_owner_plugin, + [](void* s, ss_plugin_table_entry_t* e) { + auto state = static_cast(s); + sinsp_table_entry entry(state->m_owner_plugin, + e, + state->m_table, + sinsp_table_entry::entry_dtor::NONE); + return static_cast(state->pred(entry)); + }, + &state); + } + + void erase_entry(const KeyType& key) { + ss_plugin_state_data key_data; + wrap_state_data(key, key_data); + if(m_table->erase_entry(m_owner_plugin, &key_data) != SS_PLUGIN_SUCCESS) { + throw sinsp_exception("could not erase entry: " + m_owner_plugin->m_last_owner_err); + } + } + + void clear_entries() { m_table->clear_entries(m_owner_plugin); } + +private: + libsinsp::state::sinsp_table_owner* m_owner_plugin = nullptr; + libsinsp::state::base_table* m_table = nullptr; }; diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index f5b8985289..a10a9ca622 100644 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -71,36 +71,6 @@ limitations under the License. } \ } -static inline ss_plugin_state_type typeinfo_to_state_type(const libsinsp::state::typeinfo& i) { - switch(i.index()) { - case libsinsp::state::typeinfo::index_t::TI_INT8: - return ss_plugin_state_type::SS_PLUGIN_ST_INT8; - case libsinsp::state::typeinfo::index_t::TI_INT16: - return ss_plugin_state_type::SS_PLUGIN_ST_INT16; - case libsinsp::state::typeinfo::index_t::TI_INT32: - return ss_plugin_state_type::SS_PLUGIN_ST_INT32; - case libsinsp::state::typeinfo::index_t::TI_INT64: - return ss_plugin_state_type::SS_PLUGIN_ST_INT64; - case libsinsp::state::typeinfo::index_t::TI_UINT8: - return ss_plugin_state_type::SS_PLUGIN_ST_UINT8; - case libsinsp::state::typeinfo::index_t::TI_UINT16: - return ss_plugin_state_type::SS_PLUGIN_ST_UINT16; - case libsinsp::state::typeinfo::index_t::TI_UINT32: - return ss_plugin_state_type::SS_PLUGIN_ST_UINT32; - case libsinsp::state::typeinfo::index_t::TI_UINT64: - return ss_plugin_state_type::SS_PLUGIN_ST_UINT64; - case libsinsp::state::typeinfo::index_t::TI_STRING: - return ss_plugin_state_type::SS_PLUGIN_ST_STRING; - case libsinsp::state::typeinfo::index_t::TI_BOOL: - return ss_plugin_state_type::SS_PLUGIN_ST_BOOL; - case libsinsp::state::typeinfo::index_t::TI_TABLE: - return ss_plugin_state_type::SS_PLUGIN_ST_TABLE; - default: - throw sinsp_exception("can't convert typeinfo to plugin state type: " + - std::to_string(i.index())); - } -} - template static inline void convert_types(const From& from, To& to) { to = from; @@ -248,10 +218,16 @@ static inline owned_table_input_t copy_and_check_table_input(const sinsp_plugin* return res; } -static inline std::string table_input_error_prefix(const sinsp_plugin* o, +static inline std::string table_input_error_prefix(const libsinsp::state::sinsp_table_owner* o, ss_plugin_table_input* i) { - return "error in state table '" + std::string(i->name) + "' defined by plugin '" + o->name() + - "': "; + auto plugin = dynamic_cast(o); + if(plugin) { + return "error in state table '" + std::string(i->name) + "' defined by plugin '" + + plugin->name() + "': "; + + } else { + return "error in state table '" + std::string(i->name) + "': "; + } } static const libsinsp::state::static_struct::field_infos s_empty_static_infos; @@ -260,224 +236,12 @@ static const libsinsp::state::static_struct::field_infos s_empty_static_infos; // to the libsinsp::state::table state tables definition. template struct plugin_table_wrapper : public libsinsp::state::table { - using ss = libsinsp::state::static_struct; - - using ds = libsinsp::state::dynamic_struct; - - struct plugin_field_infos : public ds::field_infos { - plugin_field_infos(const sinsp_plugin* o, const owned_table_input_t& i): - field_infos(), - m_owner(o), - m_input(i), - m_accessors() {}; - plugin_field_infos(plugin_field_infos&&) = default; - plugin_field_infos& operator=(plugin_field_infos&&) = default; - plugin_field_infos(const plugin_field_infos& s) = delete; - plugin_field_infos& operator=(const plugin_field_infos& s) = delete; - virtual ~plugin_field_infos() = default; - - const sinsp_plugin* m_owner; - owned_table_input_t m_input; - std::vector m_accessors; - - virtual const std::unordered_map& fields() override { - // list all the fields of the plugin table - uint32_t nfields = 0; - auto res = m_input->fields_ext->list_table_fields(m_input->table, &nfields); - if(res == NULL) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "list fields failure: " + m_owner->get_last_error()); - } - - // if there's a different number of fields that in our local copy, - // we re-add all of them. Duplicate definitions will be skipped - // anyways. Note, we set the index of each field info to the order - // index of the first time we received it from the plugin. This is - // relevant because the plugin API does not give guarantees about - // order stability of the returned array of field infos. - if(nfields != ds::field_infos::fields().size()) { - for(uint32_t i = 0; i < nfields; i++) { - ds::field_info f; -#define _X(_type, _dtype) \ - { f = ds::field_info::build<_type>(res[i].name, i, (uintptr_t)this, res[i].read_only); } - __PLUGIN_STATETYPE_SWITCH(res[i].field_type); -#undef _X - ds::field_infos::add_field_info(f); - } - } - - // at this point, our local copy of the field infos should be consistent - // with what's known by the plugin. So, we make sure we create an - // accessor for each of the field infos. Note, each field is associated - // an accessor that has an array position equal to the field's index. - // This will be used later for instant retrieval of the accessors - // during read-write operations. - const auto& ret = ds::field_infos::fields(); - for(const auto& it : ret) { - const auto& f = it.second; - while(m_accessors.size() <= f.index()) { - m_accessors.push_back(nullptr); - } - if(m_accessors[f.index()] == nullptr) { - auto facc = - m_input->fields_ext->get_table_field(m_input->table, - f.name().c_str(), - typeinfo_to_state_type(f.info())); - if(facc == NULL) { - throw sinsp_exception( - table_input_error_prefix(m_owner, m_input.get()) + - "get table field failure: " + m_owner->get_last_error()); - } - m_accessors[f.index()] = facc; - } - } - return ret; - } - - virtual const ds::field_info& add_field_info(const ds::field_info& field) override { - auto ret = m_input->fields_ext->add_table_field(m_input->table, - field.name().c_str(), - typeinfo_to_state_type(field.info())); - if(ret == NULL) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "add table field failure: " + m_owner->get_last_error()); - } - - // after adding a new field, we retrieve the whole list again - // to trigger the local copy updates and make sure we're in a - // consistent state. This is necessary because we we add a field, - // we have no guarantee that other components haven't added other - // fields too and we need to get their info as well. - this->fields(); - - // lastly, we leverage the base-class implementation to obtain - // a reference from our local field definitions copy. - return ds::field_infos::add_field_info(field); - } - }; - - struct plugin_table_entry : public libsinsp::state::table_entry { - plugin_table_entry(sinsp_plugin* o, - const owned_table_input_t& i, - const std::shared_ptr& fields, - ss_plugin_table_entry_t* e, - bool detached): - table_entry(fields), - m_owner(o), - m_input(i), - m_entry(e), - m_detached(detached) {}; - plugin_table_entry(const plugin_table_entry& o) = delete; - plugin_table_entry& operator=(const plugin_table_entry& o) = delete; - plugin_table_entry(plugin_table_entry&& o) = default; - plugin_table_entry& operator=(plugin_table_entry&& o) = default; - virtual ~plugin_table_entry() { - if(m_entry) { - if(m_detached) { - m_input->writer_ext->destroy_table_entry(m_input->table, m_entry); - } else { - m_input->reader_ext->release_table_entry(m_input->table, m_entry); - } - } - } - - sinsp_plugin* m_owner; - owned_table_input_t m_input; - ss_plugin_table_entry_t* m_entry; - bool m_detached; - - // note(jasondellaluce): dynamic cast is expensive but this is not expected - // to ever be ever invoked, because we set the fields shared pointer - // at construction time. This is just here as a consistency fence in - // case of misuse. - virtual void set_dynamic_fields(const std::shared_ptr& defs) override { - if(defs && dynamic_cast(defs.get()) == nullptr) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "plugin table can only be set with plugin dynamic fields"); - } - table_entry::set_dynamic_fields(defs); - } - - virtual void get_dynamic_field(const ds::field_info& i, void* out) override { - if(i.info().index() == libsinsp::state::typeinfo::index_t::TI_TABLE) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "read field failure: dynamic table fields not supported"); - } - const auto& infos = get_plugin_field_infos(); - ss_plugin_state_data dout; - auto rc = m_input->reader_ext->read_entry_field(m_input->table, - m_entry, - infos.m_accessors[i.index()], - &dout); - if(rc != SS_PLUGIN_SUCCESS) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "read field failure: " + m_owner->get_last_error()); - } - - // note: strings are the only exception to the switch case below, - // because they are represented as std::string in libsinsp' typeinfo - // and as const char*s by the plugin API. - // todo(jasondellaluce): maybe find a common place for all this - // type conversions knowledge (also leaked in dynamic_struct.h) - if(i.info().index() == libsinsp::state::typeinfo::index_t::TI_STRING) { - *(const char**)out = dout.str; - } else { -#define _X(_type, _dtype) \ - { convert_types(dout._dtype, *((_type*)out)); } - __PLUGIN_STATETYPE_SWITCH(typeinfo_to_state_type(i.info())); -#undef _X - } - } - - virtual void set_dynamic_field(const ds::field_info& i, const void* in) override { - const auto& infos = get_plugin_field_infos(); - ss_plugin_state_data v; - - // note: strings are the only exception to the switch case below, - // because they are represented as std::string in libsinsp' typeinfo - // and as const char*s by the plugin API. - // todo(jasondellaluce): maybe find a common place for all this - // type conversions knowledge (also leaked in dynamic_struct.h) - if(i.info().index() == libsinsp::state::typeinfo::index_t::TI_STRING) { - v.str = *(const char**)in; - } else { -#define _X(_type, _dtype) \ - { convert_types(*((_type*)in), v._dtype); } - __PLUGIN_STATETYPE_SWITCH(typeinfo_to_state_type(i.info())); -#undef _X - } - - auto rc = m_input->writer_ext->write_entry_field(m_input->table, - m_entry, - infos.m_accessors[i.index()], - &v); - if(rc != SS_PLUGIN_SUCCESS) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "write field failure: " + m_owner->get_last_error()); - } - } - - private: - const plugin_field_infos& get_plugin_field_infos() const { - if(dynamic_fields() == nullptr) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "local fields definitions not set"); - } - // note: casting should be safe because we force the - // plugin_field_infos subtype both the constructor and the setter - ASSERT(dynamic_cast(dynamic_fields().get()) != nullptr); - return *static_cast(dynamic_fields().get()); - } - }; - plugin_table_wrapper(sinsp_plugin* o, const ss_plugin_table_input* i): - libsinsp::state::table(i->name, &s_empty_static_infos), + libsinsp::state::table(), m_owner(o), - m_input(copy_and_check_table_input(o, i)), - m_dyn_fields(std::make_shared(o, m_input)), - m_dyn_fields_as_base_class(m_dyn_fields) { + m_input(copy_and_check_table_input(o, i)) { auto t = libsinsp::state::typeinfo::of(); - if(m_input->key_type != typeinfo_to_state_type(t)) { + if(m_input->key_type != t.type_id()) { throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "invalid key type: " + std::string(t.name())); } @@ -491,848 +255,198 @@ struct plugin_table_wrapper : public libsinsp::state::table { sinsp_plugin* m_owner; owned_table_input_t m_input; - std::shared_ptr m_dyn_fields; - std::shared_ptr m_dyn_fields_as_base_class; - - const libsinsp::state::static_struct::field_infos* static_fields() const override { - // note: always empty, plugin-defined table have no "static" fields, - // all of them are dynamically-discovered at runtime - return &s_empty_static_infos; - } - - const std::shared_ptr& dynamic_fields() const override { - return m_dyn_fields_as_base_class; - } - - size_t entries_count() const override { - auto res = m_input->reader_ext->get_table_size(m_input->table); - if(res == (uint64_t)-1) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "get size failure: " + m_owner->get_last_error()); - } - return (size_t)res; - } - - void clear_entries() override { - auto res = m_input->writer_ext->clear_table(m_input->table); - if(res != SS_PLUGIN_SUCCESS) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "clear entries failure: " + m_owner->get_last_error()); - } - } - - // used only for foreach_entry below - struct table_iterator_state { - std::string err; - plugin_table_entry* m_entry; - std::function* m_it; - }; - - // used only for foreach_entry below - static ss_plugin_bool table_iterator_func(ss_plugin_table_iterator_state_t* s, - ss_plugin_table_entry_t* _e) { - auto state = static_cast(s); - state->m_entry->m_entry = _e; - __CATCH_ERR_MSG(state->err, { return (*state->m_it)(*state->m_entry) ? 1 : 0; }); - return 0; - } - - bool foreach_entry(std::function pred) override { - plugin_table_entry entry(m_owner, m_input, m_dyn_fields, NULL, false); - table_iterator_state state; - state.m_it = &pred; - state.m_entry = &entry; - auto s = static_cast(&state); - if(m_input->reader_ext->iterate_entries(m_input->table, table_iterator_func, s) == 0) { - // avoids invoking release_table_entry - entry.m_entry = NULL; - if(!state.err.empty()) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "iterate entries failure: " + state.err); - } - return false; - } - // avoids invoking release_table_entry - entry.m_entry = NULL; - return true; - } - - std::unique_ptr new_entry() const override { - auto res = m_input->writer_ext->create_table_entry(m_input->table); - if(res == NULL) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "create entry failure: " + m_owner->get_last_error()); - } - return std::make_unique(m_owner, m_input, m_dyn_fields, res, true); - } - - std::shared_ptr get_entry(const KeyType& key) override { - ss_plugin_state_data keydata; - get_key_as_data(key, keydata); - auto res = m_input->reader_ext->get_table_entry(m_input->table, &keydata); - if(res == NULL) { - // note: libsinsp::state::table expects nullptr to be returned - // instead of an error exception - return nullptr; - } - - // note: this includes an allocation and can be quite costly in the - // critical path, however it should be used only when doing a sinsp->plugin - // access, which is expected to not be common. For plugin->plugin table - // access, we optimize for invoking the plugin's table symbol right away - return std::make_shared(m_owner, m_input, m_dyn_fields, res, false); - } - - std::shared_ptr add_entry( - const KeyType& key, - std::unique_ptr e) override { - if(!e) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "add entry invoked with null entry"); - } - - // we have no formal way for checking for misuses in which the invoker - // adds an entry that has not been created buy this table (with - // the right sub-type). Dynamic cast would be too expensive at this - // level, so we just enable it in debug mode. - ASSERT(dynamic_cast(e.get()) != nullptr); - plugin_table_entry* entry = static_cast(e.get()); - ASSERT(entry->m_entry != nullptr); - - ss_plugin_state_data keydata; - get_key_as_data(key, keydata); - auto res = m_input->writer_ext->add_table_entry(m_input->table, &keydata, entry->m_entry); - if(res == NULL) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + - "add entry failure: " + m_owner->get_last_error()); - } - entry->m_entry = res; - entry->m_detached = false; - return std::shared_ptr(std::move(e)); - } - - bool erase_entry(const KeyType& key) override { - ss_plugin_state_data keydata; - get_key_as_data(key, keydata); - auto res = m_input->writer_ext->erase_table_entry(m_input->table, &keydata); - // note: in case of failure, libsinsp::state::table expects false - // to be returned instead of an error exception - return res == SS_PLUGIN_SUCCESS; - } -private: - static void get_key_as_data(const KeyType& key, ss_plugin_state_data& out); -}; + const char* name() const override { return m_input->name; } -template<> -void plugin_table_wrapper::get_key_as_data(const int8_t& key, ss_plugin_state_data& out) { - out.s8 = key; -} + const ss_plugin_table_fieldinfo* list_fields(libsinsp::state::sinsp_table_owner* owner, + uint32_t* nfields) override; -template<> -void plugin_table_wrapper::get_key_as_data(const int16_t& key, ss_plugin_state_data& out) { - out.s16 = key; -} + ss_plugin_table_field_t* get_field(libsinsp::state::sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) override; -template<> -void plugin_table_wrapper::get_key_as_data(const int32_t& key, ss_plugin_state_data& out) { - out.s32 = key; -} + ss_plugin_table_field_t* add_field(libsinsp::state::sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) override; -template<> -void plugin_table_wrapper::get_key_as_data(const int64_t& key, ss_plugin_state_data& out) { - out.s64 = key; -} + uint64_t get_size(libsinsp::state::sinsp_table_owner* owner) override; -template<> -void plugin_table_wrapper::get_key_as_data(const uint8_t& key, ss_plugin_state_data& out) { - out.u8 = key; -} + ss_plugin_table_entry_t* get_entry(libsinsp::state::sinsp_table_owner* owner, + const ss_plugin_state_data* key) override; -template<> -void plugin_table_wrapper::get_key_as_data(const uint16_t& key, - ss_plugin_state_data& out) { - out.u16 = key; -} + void release_table_entry(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e) override; -template<> -void plugin_table_wrapper::get_key_as_data(const uint32_t& key, - ss_plugin_state_data& out) { - out.u32 = key; -} + ss_plugin_bool iterate_entries(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_iterator_func_t it, + ss_plugin_table_iterator_state_t* s) override; -template<> -void plugin_table_wrapper::get_key_as_data(const uint64_t& key, - ss_plugin_state_data& out) { - out.u64 = key; -} + ss_plugin_rc clear_entries(libsinsp::state::sinsp_table_owner* owner) override; -template<> -void plugin_table_wrapper::get_key_as_data(const std::string& key, - ss_plugin_state_data& out) { - out.str = key.c_str(); -} + ss_plugin_rc erase_entry(libsinsp::state::sinsp_table_owner* owner, + const ss_plugin_state_data* key) override; -template<> -void plugin_table_wrapper::get_key_as_data(const bool& key, ss_plugin_state_data& out) { - out.b = key; -} + ss_plugin_table_entry_t* create_table_entry(libsinsp::state::sinsp_table_owner* owner) override; -template<> -void plugin_table_wrapper::get_key_as_data( - libsinsp::state::base_table* const& key, - ss_plugin_state_data& out) { - out.table = static_cast(key); -} + void destroy_table_entry(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e) override; -// -// sinsp_field_accessor_wrapper implementation -// -sinsp_plugin::sinsp_field_accessor_wrapper::~sinsp_field_accessor_wrapper() { - if(!accessor) { - return; - } -#define _X(_type, _dtype) \ - { \ - if(dynamic) { \ - delete static_cast*>(accessor); \ - } else { \ - delete static_cast*>(accessor); \ - } \ - break; \ - } - std::string tmp; - __CATCH_ERR_MSG(tmp, { __PLUGIN_STATETYPE_SWITCH(data_type); }); -#undef _X -} + ss_plugin_table_entry_t* add_entry(libsinsp::state::sinsp_table_owner* owner, + const ss_plugin_state_data* key, + ss_plugin_table_entry_t* _e) override; -sinsp_plugin::sinsp_field_accessor_wrapper::sinsp_field_accessor_wrapper( - sinsp_plugin::sinsp_field_accessor_wrapper&& s) { - this->accessor = s.accessor; - this->dynamic = s.dynamic; - this->data_type = s.data_type; - this->subtable_key_type = s.subtable_key_type; - s.accessor = nullptr; -} + ss_plugin_rc read_entry_field(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out) override; -sinsp_plugin::sinsp_field_accessor_wrapper& sinsp_plugin::sinsp_field_accessor_wrapper::operator=( - sinsp_plugin::sinsp_field_accessor_wrapper&& s) { - this->accessor = s.accessor; - this->dynamic = s.dynamic; - this->data_type = s.data_type; - this->subtable_key_type = s.subtable_key_type; - s.accessor = nullptr; - return *this; -} - -// -// sinsp_table_wrapper implementation -// -template -void sinsp_plugin::sinsp_table_wrapper::set(sinsp_plugin* p, libsinsp::state::table* t) { - if(!t) { - throw sinsp_exception("null table assigned to sinsp table wrapper"); - } - if(!p) { - throw sinsp_exception("null plugin assigned to sinsp table wrapper"); - } - - m_table = t; - m_owner_plugin = p; - m_key_type = typeinfo_to_state_type(t->key_info()); - m_field_list.clear(); - m_table_plugin_owner = nullptr; - m_table_plugin_input = nullptr; - - // note: if the we're wrapping a plugin-implemented table under the hood, - // we just use the plugin-provided vtables right away instead of - // going through the C++ wrapper. This is both faster and safer, also - // because the current C++ wrapper for plugin-defined tables is just - // a non-functional stub used only for complying to the registry interfaces. - auto pt = dynamic_cast*>(t); - if(pt) { - m_table_plugin_owner = pt->m_owner; - m_table_plugin_input = pt->m_input.get(); - } -} - -void sinsp_plugin::sinsp_table_wrapper::unset() { - m_owner_plugin = nullptr; - m_key_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; - m_table = nullptr; - m_field_list.clear(); - m_table_plugin_owner = nullptr; - m_table_plugin_input = nullptr; -} - -bool sinsp_plugin::sinsp_table_wrapper::is_set() const { - return m_table_plugin_input != nullptr || m_table != nullptr; -} + ss_plugin_rc write_entry_field(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in) override; +}; -const ss_plugin_table_fieldinfo* sinsp_plugin::sinsp_table_wrapper::list_fields( - ss_plugin_table_t* _t, +template +const ss_plugin_table_fieldinfo* plugin_table_wrapper::list_fields( + libsinsp::state::sinsp_table_owner* owner, uint32_t* nfields) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->fields_ext->list_table_fields(pt, nfields); - if(ret == NULL) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; + auto ret = m_input->fields_ext->list_table_fields(m_input->table, nfields); + if(ret == NULL) { + owner->m_last_owner_err = m_owner->get_last_error(); } - - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - t->m_field_list.clear(); - for(auto& info : *t->m_table->static_fields()) { - ss_plugin_table_fieldinfo i; - i.name = info.second.name().c_str(); - i.field_type = typeinfo_to_state_type(info.second.info()); - i.read_only = info.second.readonly(); - t->m_field_list.push_back(i); - } - for(auto& info : t->m_table->dynamic_fields()->fields()) { - ss_plugin_table_fieldinfo i; - i.name = info.second.name().c_str(); - i.field_type = typeinfo_to_state_type(info.second.info()); - i.read_only = false; - t->m_field_list.push_back(i); - } - *nfields = t->m_field_list.size(); - return t->m_field_list.data(); - }); - return NULL; + return ret; } -ss_plugin_table_field_t* sinsp_plugin::sinsp_table_wrapper::get_field( - ss_plugin_table_t* _t, +template +ss_plugin_table_field_t* plugin_table_wrapper::get_field( + libsinsp::state::sinsp_table_owner* owner, const char* name, ss_plugin_state_type data_type) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->fields_ext->get_table_field(pt, name, data_type); - if(ret == NULL) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; + auto ret = m_input->fields_ext->get_table_field(m_input->table, name, data_type); + if(ret == NULL) { + owner->m_last_owner_err = m_owner->get_last_error(); } - - libsinsp::state::static_struct::field_infos::const_iterator fixed_it; - std::unordered_map::const_iterator - dyn_it; - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - auto it = t->m_field_accessors.find(name); - if(it != t->m_field_accessors.end()) { - return static_cast(it->second); - } - - fixed_it = t->m_table->static_fields()->find(name); - dyn_it = t->m_table->dynamic_fields()->fields().find(name); - if(fixed_it != t->m_table->static_fields()->end() && - dyn_it != t->m_table->dynamic_fields()->fields().end()) { - // todo(jasondellaluce): plugins are not aware of the difference - // between static and dynamic fields. Do we want to enforce - // this limitation in the sinsp tables implementation as well? - throw sinsp_exception("field is defined as both static and dynamic: " + - std::string(name)); - } - }); - -#define _X(_type, _dtype) \ - { \ - auto acc = fixed_it->second.new_accessor<_type>(); \ - sinsp_plugin::sinsp_field_accessor_wrapper acc_wrap; \ - acc_wrap.dynamic = false; \ - acc_wrap.data_type = data_type; \ - acc_wrap.accessor = new libsinsp::state::static_struct::field_accessor<_type>(acc); \ - t->m_owner_plugin->m_accessed_table_fields.push_back(std::move(acc_wrap)); \ - t->m_field_accessors[name] = &t->m_owner_plugin->m_accessed_table_fields.back(); \ - return t->m_field_accessors[name]; \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - if(fixed_it != t->m_table->static_fields()->end()) { - if(data_type != typeinfo_to_state_type(fixed_it->second.info())) { - throw sinsp_exception("incompatible data types for static field: " + - std::string(name)); - } - __PLUGIN_STATETYPE_SWITCH(data_type); - } - }); -#undef _X - -#define _X(_type, _dtype) \ - { \ - auto acc = dyn_it->second.new_accessor<_type>(); \ - sinsp_plugin::sinsp_field_accessor_wrapper acc_wrap; \ - acc_wrap.dynamic = true; \ - acc_wrap.data_type = data_type; \ - acc_wrap.accessor = new libsinsp::state::dynamic_struct::field_accessor<_type>(acc); \ - t->m_owner_plugin->m_accessed_table_fields.push_back(std::move(acc_wrap)); \ - t->m_field_accessors[name] = &t->m_owner_plugin->m_accessed_table_fields.back(); \ - return t->m_field_accessors[name]; \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - if(dyn_it != t->m_table->dynamic_fields()->fields().end()) { - if(data_type != typeinfo_to_state_type(dyn_it->second.info())) { - throw sinsp_exception("incompatible data types for dynamic field: " + - std::string(name)); - } - __PLUGIN_STATETYPE_SWITCH(data_type); - } - throw sinsp_exception("undefined field '" + std::string(name) + "' in table '" + - t->m_table->name() + "'"); - }); -#undef _X - - return NULL; + return ret; } -ss_plugin_table_field_t* sinsp_plugin::sinsp_table_wrapper::add_field( - ss_plugin_table_t* _t, +template +ss_plugin_table_field_t* plugin_table_wrapper::add_field( + libsinsp::state::sinsp_table_owner* owner, const char* name, ss_plugin_state_type data_type) { - auto t = static_cast(_t); - - if(data_type == ss_plugin_state_type::SS_PLUGIN_ST_TABLE) { - t->m_owner_plugin->m_last_owner_err = - "can't add dynamic field of type table: " + std::string(name); - return NULL; - } - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->fields_ext->add_table_field(pt, name, data_type); - if(ret == NULL) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; + auto ret = m_input->fields_ext->add_table_field(m_input->table, name, data_type); + if(ret == NULL) { + owner->m_last_owner_err = m_owner->get_last_error(); } - - if(t->m_table->static_fields()->find(name) != t->m_table->static_fields()->end()) { - t->m_owner_plugin->m_last_owner_err = - "can't add dynamic field already defined as static: " + std::string(name); - return NULL; - } - -#define _X(_type, _dtype) \ - { \ - t->m_table->dynamic_fields()->add_field<_type>(name); \ - break; \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - __PLUGIN_STATETYPE_SWITCH(data_type); - return get_field(_t, name, data_type); - }); -#undef _X - return NULL; + return ret; } -const char* sinsp_plugin::sinsp_table_wrapper::get_name(ss_plugin_table_t* _t) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - return t->m_table_plugin_input->name; - } - - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { return t->m_table->name().c_str(); }); - return NULL; -} - -uint64_t sinsp_plugin::sinsp_table_wrapper::get_size(ss_plugin_table_t* _t) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->reader_ext->get_table_size(pt); - if(ret == ((uint64_t)-1)) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; +template +uint64_t plugin_table_wrapper::get_size(libsinsp::state::sinsp_table_owner* owner) { + auto ret = m_input->reader_ext->get_table_size(m_input->table); + if(ret == ((uint64_t)-1)) { + owner->m_last_owner_err = m_owner->get_last_error(); } - - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { return t->m_table->entries_count(); }); - return ((uint64_t)-1); + return ret; } -ss_plugin_table_entry_t* sinsp_plugin::sinsp_table_wrapper::get_entry( - ss_plugin_table_t* _t, +template +ss_plugin_table_entry_t* plugin_table_wrapper::get_entry( + libsinsp::state::sinsp_table_owner* owner, const ss_plugin_state_data* key) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->reader_ext->get_table_entry(pt, key); - if(ret == NULL) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; + auto ret = m_input->reader_ext->get_table_entry(m_input->table, key); + if(ret == NULL) { + owner->m_last_owner_err = m_owner->get_last_error(); } - -// note: the C++ API returns a shared pointer, but in plugins we only -// use raw pointers without increasing/decreasing/owning the refcount. -// How can we do better than this? -// todo(jasondellaluce): should we actually make plugins own some memory, -// to guarantee that the shared_ptr returned is properly refcounted? -#define _X(_type, _dtype) \ - { \ - auto tt = static_cast*>(t->m_table); \ - _type kk; \ - convert_types(key->_dtype, kk); \ - auto ret = tt->get_entry(kk); \ - if(ret != nullptr) { \ - auto owned_ptr = t->m_owner_plugin->find_unset_accessed_table_entry(); \ - *owned_ptr = ret; \ - return static_cast(owned_ptr); \ - } \ - throw sinsp_exception("get_entry found no element at given key"); \ - return NULL; \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(t->m_key_type); }); -#undef _X - return NULL; + return ret; } -void sinsp_plugin::sinsp_table_wrapper::release_table_entry(ss_plugin_table_t* _t, - ss_plugin_table_entry_t* _e) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - t->m_table_plugin_input->reader_ext->release_table_entry(pt, _e); - return; - } - - static_cast*>(_e)->reset(); +template +void plugin_table_wrapper::release_table_entry(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e) { + m_input->reader_ext->release_table_entry(m_input->table, _e); } -ss_plugin_bool sinsp_plugin::sinsp_table_wrapper::iterate_entries( - ss_plugin_table_t* _t, +template +ss_plugin_bool plugin_table_wrapper::iterate_entries( + libsinsp::state::sinsp_table_owner* owner, ss_plugin_table_iterator_func_t it, ss_plugin_table_iterator_state_t* s) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - return t->m_table_plugin_input->reader_ext->iterate_entries(pt, it, s); - } - - std::shared_ptr owned_ptr; - std::function iter = [&owned_ptr, &it, &s](auto& e) { - owned_ptr.reset(&e, [](libsinsp::state::table_entry* p) {}); - return it(s, static_cast(&owned_ptr)) != 0; - }; - -#define _X(_type, _dtype) \ - { \ - auto tt = static_cast*>(t->m_table); \ - return tt->foreach_entry(iter); \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(t->m_key_type); }); -#undef _X - - return false; + return m_input->reader_ext->iterate_entries(m_input->table, it, s); } -ss_plugin_rc sinsp_plugin::sinsp_table_wrapper::clear(ss_plugin_table_t* _t) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer_ext->clear_table(pt); - if(ret == SS_PLUGIN_FAILURE) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; +template +ss_plugin_rc plugin_table_wrapper::clear_entries( + libsinsp::state::sinsp_table_owner* owner) { + auto ret = m_input->writer_ext->clear_table(m_input->table); + if(ret == SS_PLUGIN_FAILURE) { + owner->m_last_owner_err = m_owner->get_last_error(); } - - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - t->m_table->clear_entries(); - return SS_PLUGIN_SUCCESS; - }); - return SS_PLUGIN_FAILURE; + return ret; } -ss_plugin_rc sinsp_plugin::sinsp_table_wrapper::erase_entry(ss_plugin_table_t* _t, - const ss_plugin_state_data* key) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer_ext->erase_table_entry(pt, key); - if(ret == SS_PLUGIN_FAILURE) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; - } - -#define _X(_type, _dtype) \ - { \ - _type kk; \ - convert_types(key->_dtype, kk); \ - if(static_cast*>(t->m_table)->erase_entry(kk)) { \ - return SS_PLUGIN_SUCCESS; \ - } else { \ - t->m_owner_plugin->m_last_owner_err = "table entry not found"; \ - return SS_PLUGIN_FAILURE; \ - } \ +template +ss_plugin_rc plugin_table_wrapper::erase_entry(libsinsp::state::sinsp_table_owner* owner, + const ss_plugin_state_data* key) { + auto ret = m_input->writer_ext->erase_table_entry(m_input->table, key); + if(ret == SS_PLUGIN_FAILURE) { + owner->m_last_owner_err = m_owner->get_last_error(); } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(t->m_key_type); }); -#undef _X - return SS_PLUGIN_FAILURE; + return ret; } -ss_plugin_table_entry_t* sinsp_plugin::sinsp_table_wrapper::create_table_entry( - ss_plugin_table_t* _t) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer_ext->create_table_entry(pt); - if(ret == NULL) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; - } - -#define _X(_type, _dtype) \ - { \ - auto tt = static_cast*>(t->m_table); \ - auto ret = tt->new_entry().release(); \ - auto owned_ptr = t->m_owner_plugin->find_unset_accessed_table_entry(); \ - owned_ptr->reset(ret, [](libsinsp::state::table_entry* p) { /* do nothing */ }); \ - return static_cast(owned_ptr); \ +template +ss_plugin_table_entry_t* plugin_table_wrapper::create_table_entry( + libsinsp::state::sinsp_table_owner* owner) { + auto ret = m_input->writer_ext->create_table_entry(m_input->table); + if(ret == NULL) { + owner->m_last_owner_err = m_owner->get_last_error(); } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(t->m_key_type); }); -#undef _X - return NULL; + return ret; } -void sinsp_plugin::sinsp_table_wrapper::destroy_table_entry(ss_plugin_table_t* _t, - ss_plugin_table_entry_t* _e) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - t->m_table_plugin_input->writer_ext->destroy_table_entry(pt, _e); - return; - } - -#define _X(_type, _dtype) \ - { \ - auto e = static_cast*>(_e); \ - auto ptr = std::unique_ptr(e->get()); \ - e->reset(); \ - break; \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(t->m_key_type); }); -#undef _X +template +void plugin_table_wrapper::destroy_table_entry(libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e) { + m_input->writer_ext->destroy_table_entry(m_input->table, _e); } -ss_plugin_table_entry_t* sinsp_plugin::sinsp_table_wrapper::add_entry( - ss_plugin_table_t* _t, +template +ss_plugin_table_entry_t* plugin_table_wrapper::add_entry( + libsinsp::state::sinsp_table_owner* owner, const ss_plugin_state_data* key, ss_plugin_table_entry_t* _e) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer_ext->add_table_entry(pt, key, _e); - if(ret == NULL) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; - } - -#define _X(_type, _dtype) \ - { \ - auto e = static_cast*>(_e); \ - auto ptr = std::unique_ptr(e->get()); \ - e->reset(); \ - auto tt = static_cast*>(t->m_table); \ - _type kk; \ - convert_types(key->_dtype, kk); \ - auto owned_ptr = t->m_owner_plugin->find_unset_accessed_table_entry(); \ - *owned_ptr = tt->add_entry(kk, std::move(ptr)); \ - return static_cast(owned_ptr); \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(t->m_key_type); }); -#undef _X - return NULL; -} - -ss_plugin_rc sinsp_plugin::sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, - ss_plugin_table_entry_t* _e, - const ss_plugin_table_field_t* f, - ss_plugin_state_data* out) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->reader_ext->read_entry_field(pt, _e, f, out); - if(ret == SS_PLUGIN_FAILURE) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; - } - - auto a = static_cast(f); - auto e = static_cast*>(_e); - auto res = SS_PLUGIN_FAILURE; - -#define _X(_type, _dtype) \ - { \ - if(a->dynamic) { \ - auto aa = static_cast*>( \ - a->accessor); \ - e->get()->get_dynamic_field<_type>(*aa, out->_dtype); \ - } else { \ - auto aa = static_cast*>( \ - a->accessor); \ - e->get()->get_static_field<_type>(*aa, out->_dtype); \ - } \ - res = SS_PLUGIN_SUCCESS; \ - break; \ + auto ret = m_input->writer_ext->add_table_entry(m_input->table, key, _e); + if(ret == NULL) { + owner->m_last_owner_err = m_owner->get_last_error(); } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(a->data_type); }); -#undef _X - -#define _X(_type, _dtype) \ - { \ - auto st = static_cast*>(subtable_ptr); \ - auto& slot = t->m_owner_plugin->find_unset_ephemeral_table(); \ - slot.wrapper.set<_type>(t->m_owner_plugin, st); \ - slot.update(); \ - out->table = &slot.input; \ - }; - if(a->data_type == ss_plugin_state_type::SS_PLUGIN_ST_TABLE) { - auto* subtable_ptr = out->table; - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(a->subtable_key_type); }); - } -#undef _X - - return res; + return ret; } -ss_plugin_rc sinsp_plugin::sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, - ss_plugin_table_entry_t* _e, - const ss_plugin_table_field_t* f, - const ss_plugin_state_data* in) { - auto t = static_cast(_t); - - if(t->m_table_plugin_input) { - auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer_ext->write_entry_field(pt, _e, f, in); - if(ret == SS_PLUGIN_FAILURE) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; - } - - auto a = static_cast(f); - auto e = static_cast*>(_e); - - // todo(jasondellaluce): drop this check once we start supporting this - if(a->data_type == ss_plugin_state_type::SS_PLUGIN_ST_TABLE) { - t->m_owner_plugin->m_last_owner_err = "writing to table fields is currently not supported"; - return SS_PLUGIN_FAILURE; - } - -#define _X(_type, _dtype) \ - { \ - if(a->dynamic) { \ - auto aa = static_cast*>( \ - a->accessor); \ - _type val; \ - convert_types(in->_dtype, val); \ - e->get()->set_dynamic_field<_type>(*aa, val); \ - } else { \ - auto aa = static_cast*>( \ - a->accessor); \ - _type val; \ - convert_types(in->_dtype, val); \ - e->get()->set_static_field<_type>(*aa, val); \ - } \ - return SS_PLUGIN_SUCCESS; \ +template +ss_plugin_rc plugin_table_wrapper::read_entry_field( + libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out) { + auto ret = m_input->reader_ext->read_entry_field(m_input->table, _e, f, out); + if(ret == SS_PLUGIN_FAILURE) { + owner->m_last_owner_err = m_owner->get_last_error(); } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, - { __PLUGIN_STATETYPE_SWITCH(a->data_type); }); -#undef _X - return SS_PLUGIN_FAILURE; + return ret; } -// -// sinsp_table_input implementation -// -sinsp_plugin::sinsp_table_input::sinsp_table_input() { - // populate vtables - reader_vtable.get_table_name = sinsp_plugin::sinsp_table_wrapper::get_name; - reader_vtable.get_table_size = sinsp_plugin::sinsp_table_wrapper::get_size; - reader_vtable.get_table_entry = sinsp_plugin::sinsp_table_wrapper::get_entry; - reader_vtable.read_entry_field = sinsp_plugin::sinsp_table_wrapper::read_entry_field; - reader_vtable.release_table_entry = sinsp_plugin::sinsp_table_wrapper::release_table_entry; - reader_vtable.iterate_entries = sinsp_plugin::sinsp_table_wrapper::iterate_entries; - writer_vtable.clear_table = sinsp_plugin::sinsp_table_wrapper::clear; - writer_vtable.erase_table_entry = sinsp_plugin::sinsp_table_wrapper::erase_entry; - writer_vtable.create_table_entry = sinsp_plugin::sinsp_table_wrapper::create_table_entry; - writer_vtable.destroy_table_entry = sinsp_plugin::sinsp_table_wrapper::destroy_table_entry; - writer_vtable.add_table_entry = sinsp_plugin::sinsp_table_wrapper::add_entry; - writer_vtable.write_entry_field = sinsp_plugin::sinsp_table_wrapper::write_entry_field; - fields_vtable.list_table_fields = sinsp_plugin::sinsp_table_wrapper::list_fields; - fields_vtable.add_table_field = sinsp_plugin::sinsp_table_wrapper::add_field; - fields_vtable.get_table_field = sinsp_plugin::sinsp_table_wrapper::get_field; - - // fill-up input's legacy vtables for backward compatibility - input.reader.get_table_name = reader_vtable.get_table_name; - input.reader.get_table_size = reader_vtable.get_table_size; - input.reader.get_table_entry = reader_vtable.get_table_entry; - input.reader.read_entry_field = reader_vtable.read_entry_field; - input.writer.clear_table = writer_vtable.clear_table; - input.writer.erase_table_entry = writer_vtable.erase_table_entry; - input.writer.create_table_entry = writer_vtable.create_table_entry; - input.writer.destroy_table_entry = writer_vtable.destroy_table_entry; - input.writer.add_table_entry = writer_vtable.add_table_entry; - input.writer.write_entry_field = writer_vtable.write_entry_field; - input.fields.list_table_fields = fields_vtable.list_table_fields; - input.fields.add_table_field = fields_vtable.add_table_field; - input.fields.get_table_field = fields_vtable.get_table_field; - - // bind input's vtables - input.fields_ext = &fields_vtable; - input.reader_ext = &reader_vtable; - input.writer_ext = &writer_vtable; - - // fill-up with some default values - input.table = nullptr; - input.name = nullptr; - input.key_type = wrapper.m_key_type; -} - -void sinsp_plugin::sinsp_table_input::update() { - input.name = nullptr; - input.table = nullptr; - if(!wrapper.is_set()) { - return; - } - - input.table = &wrapper; - if(wrapper.m_table) { - input.key_type = wrapper.m_key_type; - input.name = wrapper.m_table->name().c_str(); - } else if(wrapper.m_table_plugin_input) { - input.key_type = wrapper.m_table_plugin_input->key_type; - input.name = wrapper.m_table_plugin_input->name; +template +ss_plugin_rc plugin_table_wrapper::write_entry_field( + libsinsp::state::sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in) { + auto ret = m_input->writer_ext->write_entry_field(m_input->table, _e, f, in); + if(ret == SS_PLUGIN_FAILURE) { + owner->m_last_owner_err = m_owner->get_last_error(); } + return ret; } // the following table api symbols act as dispatcher for the table API @@ -1483,8 +597,8 @@ ss_plugin_table_info* sinsp_plugin::table_api_list_tables(ss_plugin_owner_t* o, p->m_table_infos.clear(); for(const auto& d : p->m_table_registry->tables()) { ss_plugin_table_info info; - info.name = d.second->name().c_str(); - info.key_type = typeinfo_to_state_type(d.second->key_info()); + info.name = d.second->name(); + info.key_type = d.second->key_info().type_id(); p->m_table_infos.push_back(info); } *ntables = p->m_table_infos.size(); @@ -1510,8 +624,7 @@ ss_plugin_table_t* sinsp_plugin::table_api_get_table(ss_plugin_owner_t* o, if(!t) { \ return NULL; \ } \ - p->m_accessed_tables[name].wrapper.set(p, t); \ - p->m_accessed_tables[name].update(); \ + p->m_accessed_tables[name].set(p, t); \ return static_cast(&p->m_accessed_tables[name].input); \ }; __CATCH_ERR_MSG(p->m_last_owner_err, { diff --git a/userspace/libsinsp/state/dynamic_struct.h b/userspace/libsinsp/state/dynamic_struct.h index 55ac0fd545..6321a6b8dc 100644 --- a/userspace/libsinsp/state/dynamic_struct.h +++ b/userspace/libsinsp/state/dynamic_struct.h @@ -23,6 +23,8 @@ limitations under the License. #include #include #include +#include +#include namespace libsinsp { namespace state { @@ -96,7 +98,7 @@ class dynamic_struct { inline bool valid() const { // note(jasondellaluce): for now dynamic fields of type table are // not supported, so we consider them to be invalid - return m_index != (size_t)-1 && m_info.index() != typeinfo::index_t::TI_TABLE; + return m_index != (size_t)-1 && m_info.type_id() != SS_PLUGIN_ST_TABLE; } /** @@ -210,7 +212,7 @@ class dynamic_struct { protected: virtual const field_info& add_field_info(const field_info& field) { - if(field.info().index() == typeinfo::index_t::TI_TABLE) { + if(field.info().type_id() == SS_PLUGIN_ST_TABLE) { throw sinsp_exception("dynamic fields of type table are not supported"); } @@ -308,7 +310,7 @@ class dynamic_struct { */ virtual void get_dynamic_field(const field_info& i, void* out) { const auto* buf = _access_dynamic_field(i.m_index); - if(i.info().index() == typeinfo::index_t::TI_STRING) { + if(i.info().type_id() == SS_PLUGIN_ST_STRING) { *((const char**)out) = ((const std::string*)buf)->c_str(); } else { memcpy(out, buf, i.info().size()); @@ -323,7 +325,7 @@ class dynamic_struct { */ virtual void set_dynamic_field(const field_info& i, const void* in) { auto* buf = _access_dynamic_field(i.m_index); - if(i.info().index() == typeinfo::index_t::TI_STRING) { + if(i.info().type_id() == SS_PLUGIN_ST_STRING) { *((std::string*)buf) = *((const char**)in); } else { memcpy(buf, in, i.info().size()); diff --git a/userspace/libsinsp/state/table.cpp b/userspace/libsinsp/state/table.cpp new file mode 100644 index 0000000000..b607f11116 --- /dev/null +++ b/userspace/libsinsp/state/table.cpp @@ -0,0 +1,684 @@ +/* +Copyright (C) 2024 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include +#include + +#define __CATCH_ERR_MSG(_ERR, _F) \ + { \ + try { \ + _F; \ + } catch(const std::exception& _e) { \ + _ERR = _e.what(); \ + } catch(...) { \ + _ERR = "unknown error"; \ + } \ + } + +#define __PLUGIN_STATETYPE_SWITCH(_kt) \ + { \ + switch(_kt) { \ + case ss_plugin_state_type::SS_PLUGIN_ST_INT8: \ + _X(int8_t, s8); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_INT16: \ + _X(int16_t, s16); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_INT32: \ + _X(int32_t, s32); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_INT64: \ + _X(int64_t, s64); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_UINT8: \ + _X(uint8_t, u8); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_UINT16: \ + _X(uint16_t, u16); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_UINT32: \ + _X(uint32_t, u32); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_UINT64: \ + _X(uint64_t, u64); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_STRING: \ + _X(std::string, str); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_BOOL: \ + _X(bool, b); \ + break; \ + case ss_plugin_state_type::SS_PLUGIN_ST_TABLE: \ + _X(libsinsp::state::base_table*, table); \ + break; \ + default: \ + throw sinsp_exception("can't convert plugin state type to typeinfo: " + \ + std::to_string(_kt)); \ + } \ + } + +template +static inline void convert_types(const From& from, To& to) { + to = from; +} + +// special cases for strings +template<> +inline void convert_types(const std::string& from, const char*& to) { + to = from.c_str(); +} + +template<> +inline void convert_types(libsinsp::state::base_table* const& from, ss_plugin_table_t*& to) { + to = static_cast(from); +} + +template<> +inline void convert_types(ss_plugin_table_t* const& from, libsinsp::state::base_table*& to) { + to = static_cast(from); +} + +template +void extract_key(const ss_plugin_state_data& key, KeyType& out) { + throw sinsp_exception("unsupported key type"); +} + +template<> +void extract_key(const ss_plugin_state_data& key, uint64_t& out) { + out = key.u64; +} + +template<> +void extract_key(const ss_plugin_state_data& key, int64_t& out) { + out = key.u64; +} + +// +// sinsp_field_accessor_wrapper implementation +// +libsinsp::state::sinsp_field_accessor_wrapper::~sinsp_field_accessor_wrapper() { + if(!accessor) { + return; + } +#define _X(_type, _dtype) \ + { \ + if(dynamic) { \ + delete static_cast*>(accessor); \ + } else { \ + delete static_cast*>(accessor); \ + } \ + break; \ + } + std::string tmp; + __CATCH_ERR_MSG(tmp, { __PLUGIN_STATETYPE_SWITCH(data_type); }); +#undef _X +} + +libsinsp::state::sinsp_field_accessor_wrapper::sinsp_field_accessor_wrapper( + libsinsp::state::sinsp_field_accessor_wrapper&& s) { + this->accessor = s.accessor; + this->dynamic = s.dynamic; + this->data_type = s.data_type; + this->subtable_key_type = s.subtable_key_type; + s.accessor = nullptr; +} + +libsinsp::state::sinsp_field_accessor_wrapper& +libsinsp::state::sinsp_field_accessor_wrapper::operator=( + libsinsp::state::sinsp_field_accessor_wrapper&& s) { + this->accessor = s.accessor; + this->dynamic = s.dynamic; + this->data_type = s.data_type; + this->subtable_key_type = s.subtable_key_type; + s.accessor = nullptr; + return *this; +} + +// +// table_accessor implementation +// +template +void libsinsp::state::table_accessor::set(sinsp_table_owner* p, libsinsp::state::table* t) { + if(!t) { + throw sinsp_exception("null table assigned to sinsp table wrapper"); + } + if(!p) { + throw sinsp_exception("null plugin assigned to sinsp table wrapper"); + } + + m_table = t; + m_owner_plugin = p; + + input.name = m_table->name(); + input.table = this; + input.key_type = m_table->key_info().type_id(); +} + +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set( + sinsp_table_owner* p, + libsinsp::state::table* t); +template void libsinsp::state::table_accessor::set(sinsp_table_owner* p, + libsinsp::state::table* t); +// Do not instantiate the template for libsinsp::state::base_table* since a table cannot be used +// as a key for another table + +void libsinsp::state::table_accessor::unset() { + m_owner_plugin = nullptr; + m_table = nullptr; + + input.name = nullptr; + input.table = nullptr; + input.key_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; +} + +bool libsinsp::state::table_accessor::is_set() const { + return m_table != nullptr; +} + +const ss_plugin_table_fieldinfo* libsinsp::state::table_accessor::list_fields(ss_plugin_table_t* _t, + uint32_t* nfields) { + auto t = static_cast(_t); + return t->m_table->list_fields(t->m_owner_plugin, nfields); +} + +ss_plugin_table_field_t* libsinsp::state::table_accessor::get_field( + ss_plugin_table_t* _t, + const char* name, + ss_plugin_state_type data_type) { + auto t = static_cast(_t); + return t->m_table->get_field(t->m_owner_plugin, name, data_type); +} + +ss_plugin_table_field_t* libsinsp::state::table_accessor::add_field( + ss_plugin_table_t* _t, + const char* name, + ss_plugin_state_type data_type) { + auto t = static_cast(_t); + + if(data_type == ss_plugin_state_type::SS_PLUGIN_ST_TABLE) { + t->m_owner_plugin->m_last_owner_err = + "can't add dynamic field of type table: " + std::string(name); + return NULL; + } + + return t->m_table->add_field(t->m_owner_plugin, name, data_type); +} + +const char* libsinsp::state::table_accessor::get_name(ss_plugin_table_t* _t) { + auto t = static_cast(_t); + return t->m_table->name(); +} + +uint64_t libsinsp::state::table_accessor::get_size(ss_plugin_table_t* _t) { + auto t = static_cast(_t); + return t->m_table->get_size(t->m_owner_plugin); +} + +ss_plugin_table_entry_t* libsinsp::state::table_accessor::get_entry( + ss_plugin_table_t* _t, + const ss_plugin_state_data* key) { + auto t = static_cast(_t); + return t->m_table->get_entry(t->m_owner_plugin, key); +} + +void libsinsp::state::table_accessor::release_table_entry(ss_plugin_table_t* _t, + ss_plugin_table_entry_t* _e) { + auto t = static_cast(_t); + t->m_table->release_table_entry(t->m_owner_plugin, _e); +} + +ss_plugin_bool libsinsp::state::table_accessor::iterate_entries( + ss_plugin_table_t* _t, + ss_plugin_table_iterator_func_t it, + ss_plugin_table_iterator_state_t* s) { + auto t = static_cast(_t); + return t->m_table->iterate_entries(t->m_owner_plugin, it, s); +} + +ss_plugin_rc libsinsp::state::table_accessor::clear(ss_plugin_table_t* _t) { + auto t = static_cast(_t); + return t->m_table->clear_entries(t->m_owner_plugin); +} + +ss_plugin_rc libsinsp::state::table_accessor::erase_entry(ss_plugin_table_t* _t, + const ss_plugin_state_data* key) { + auto t = static_cast(_t); + return t->m_table->erase_entry(t->m_owner_plugin, key); +} + +ss_plugin_table_entry_t* libsinsp::state::table_accessor::create_table_entry( + ss_plugin_table_t* _t) { + auto t = static_cast(_t); + return t->m_table->create_table_entry(t->m_owner_plugin); +} + +void libsinsp::state::table_accessor::destroy_table_entry(ss_plugin_table_t* _t, + ss_plugin_table_entry_t* _e) { + auto t = static_cast(_t); + return t->m_table->destroy_table_entry(t->m_owner_plugin, _e); +} + +ss_plugin_table_entry_t* libsinsp::state::table_accessor::add_entry(ss_plugin_table_t* _t, + const ss_plugin_state_data* key, + ss_plugin_table_entry_t* _e) { + auto t = static_cast(_t); + return t->m_table->add_entry(t->m_owner_plugin, key, _e); +} + +ss_plugin_rc libsinsp::state::table_accessor::read_entry_field(ss_plugin_table_t* _t, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out) { + auto t = static_cast(_t); + return t->m_table->read_entry_field(t->m_owner_plugin, _e, f, out); +} + +ss_plugin_rc libsinsp::state::table_accessor::write_entry_field(ss_plugin_table_t* _t, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in) { + auto t = static_cast(_t); + return t->m_table->write_entry_field(t->m_owner_plugin, _e, f, in); +} + +// +// sinsp_table_input implementation +// +libsinsp::state::table_accessor::table_accessor() { + // populate vtables + reader_vtable.get_table_name = libsinsp::state::table_accessor::get_name; + reader_vtable.get_table_size = libsinsp::state::table_accessor::get_size; + reader_vtable.get_table_entry = libsinsp::state::table_accessor::get_entry; + reader_vtable.read_entry_field = libsinsp::state::table_accessor::read_entry_field; + reader_vtable.release_table_entry = libsinsp::state::table_accessor::release_table_entry; + reader_vtable.iterate_entries = libsinsp::state::table_accessor::iterate_entries; + writer_vtable.clear_table = libsinsp::state::table_accessor::clear; + writer_vtable.erase_table_entry = libsinsp::state::table_accessor::erase_entry; + writer_vtable.create_table_entry = libsinsp::state::table_accessor::create_table_entry; + writer_vtable.destroy_table_entry = libsinsp::state::table_accessor::destroy_table_entry; + writer_vtable.add_table_entry = libsinsp::state::table_accessor::add_entry; + writer_vtable.write_entry_field = libsinsp::state::table_accessor::write_entry_field; + fields_vtable.list_table_fields = libsinsp::state::table_accessor::list_fields; + fields_vtable.add_table_field = libsinsp::state::table_accessor::add_field; + fields_vtable.get_table_field = libsinsp::state::table_accessor::get_field; + + // fill-up input's legacy vtables for backward compatibility + input.reader.get_table_name = reader_vtable.get_table_name; + input.reader.get_table_size = reader_vtable.get_table_size; + input.reader.get_table_entry = reader_vtable.get_table_entry; + input.reader.read_entry_field = reader_vtable.read_entry_field; + input.writer.clear_table = writer_vtable.clear_table; + input.writer.erase_table_entry = writer_vtable.erase_table_entry; + input.writer.create_table_entry = writer_vtable.create_table_entry; + input.writer.destroy_table_entry = writer_vtable.destroy_table_entry; + input.writer.add_table_entry = writer_vtable.add_table_entry; + input.writer.write_entry_field = writer_vtable.write_entry_field; + input.fields.list_table_fields = fields_vtable.list_table_fields; + input.fields.add_table_field = fields_vtable.add_table_field; + input.fields.get_table_field = fields_vtable.get_table_field; + + // bind input's vtables + input.fields_ext = &fields_vtable; + input.reader_ext = &reader_vtable; + input.writer_ext = &writer_vtable; + + // fill-up with some default values + input.table = nullptr; + input.name = nullptr; + input.key_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; +} + +template +const ss_plugin_table_fieldinfo* libsinsp::state::built_in_table::list_fields( + sinsp_table_owner* owner, + uint32_t* nfields) { + __CATCH_ERR_MSG(owner->m_last_owner_err, { + this->m_field_list.clear(); + for(auto& info : *this->static_fields()) { + ss_plugin_table_fieldinfo i; + i.name = info.second.name().c_str(); + i.field_type = info.second.info().type_id(); + i.read_only = info.second.readonly(); + this->m_field_list.push_back(i); + } + for(auto& info : this->dynamic_fields()->fields()) { + ss_plugin_table_fieldinfo i; + i.name = info.second.name().c_str(); + i.field_type = info.second.info().type_id(); + i.read_only = false; + this->m_field_list.push_back(i); + } + *nfields = this->m_field_list.size(); + return this->m_field_list.data(); + }); + return NULL; +} + +template +ss_plugin_table_field_t* libsinsp::state::built_in_table::get_field( + sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) { + libsinsp::state::static_struct::field_infos::const_iterator fixed_it; + std::unordered_map::const_iterator + dyn_it; + __CATCH_ERR_MSG(owner->m_last_owner_err, { + auto it = this->m_field_accessors.find(name); + if(it != this->m_field_accessors.end()) { + return static_cast(it->second); + } + + fixed_it = this->static_fields()->find(name); + dyn_it = this->dynamic_fields()->fields().find(name); + if(fixed_it != this->static_fields()->end() && + dyn_it != this->dynamic_fields()->fields().end()) { + // todo(jasondellaluce): plugins are not aware of the difference + // between static and dynamic fields. Do we want to enforce + // this limitation in the sinsp tables implementation as well? + throw sinsp_exception("field is defined as both static and dynamic: " + + std::string(name)); + } + }); + +#define _X(_type, _dtype) \ + { \ + auto acc = fixed_it->second.new_accessor<_type>(); \ + libsinsp::state::sinsp_field_accessor_wrapper acc_wrap; \ + acc_wrap.dynamic = false; \ + acc_wrap.data_type = data_type; \ + acc_wrap.accessor = new libsinsp::state::static_struct::field_accessor<_type>(acc); \ + owner->m_accessed_table_fields.push_back(std::move(acc_wrap)); \ + this->m_field_accessors[name] = &owner->m_accessed_table_fields.back(); \ + return this->m_field_accessors[name]; \ + } + __CATCH_ERR_MSG(owner->m_last_owner_err, { + if(fixed_it != this->static_fields()->end()) { + if(data_type != fixed_it->second.info().type_id()) { + throw sinsp_exception("incompatible data types for static field: " + + std::string(name)); + } + __PLUGIN_STATETYPE_SWITCH(data_type); + } + }); +#undef _X + +#define _X(_type, _dtype) \ + { \ + auto acc = dyn_it->second.new_accessor<_type>(); \ + libsinsp::state::sinsp_field_accessor_wrapper acc_wrap; \ + acc_wrap.dynamic = true; \ + acc_wrap.data_type = data_type; \ + acc_wrap.accessor = new libsinsp::state::dynamic_struct::field_accessor<_type>(acc); \ + owner->m_accessed_table_fields.push_back(std::move(acc_wrap)); \ + this->m_field_accessors[name] = &owner->m_accessed_table_fields.back(); \ + return this->m_field_accessors[name]; \ + } + __CATCH_ERR_MSG(owner->m_last_owner_err, { + if(dyn_it != this->dynamic_fields()->fields().end()) { + if(data_type != dyn_it->second.info().type_id()) { + throw sinsp_exception("incompatible data types for dynamic field: " + + std::string(name)); + } + __PLUGIN_STATETYPE_SWITCH(data_type); + } + throw sinsp_exception("undefined field '" + std::string(name) + "' in table '" + + std::string(this->name()) + "'"); + }); +#undef _X + + return NULL; +} + +template +ss_plugin_table_field_t* libsinsp::state::built_in_table::add_field( + sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) { + if(this->static_fields()->find(name) != this->static_fields()->end()) { + owner->m_last_owner_err = + "can't add dynamic field already defined as static: " + std::string(name); + return NULL; + } + +#define _X(_type, _dtype) \ + { \ + this->dynamic_fields()->template add_field<_type>(name); \ + break; \ + } + __CATCH_ERR_MSG(owner->m_last_owner_err, { + __PLUGIN_STATETYPE_SWITCH(data_type); + return get_field(owner, name, data_type); + }); +#undef _X + return NULL; +} + +template +uint64_t libsinsp::state::built_in_table::get_size(sinsp_table_owner* owner) { + return this->entries_count(); +} + +template +ss_plugin_table_entry_t* libsinsp::state::built_in_table::get_entry( + sinsp_table_owner* owner, + const ss_plugin_state_data* key) { + // note: the C++ API returns a shared pointer, but in plugins we only + // use raw pointers without increasing/decreasing/owning the refcount. + // How can we do better than this? + // todo(jasondellaluce): should we actually make plugins own some memory, + // to guarantee that the shared_ptr returned is properly refcounted? + __CATCH_ERR_MSG(owner->m_last_owner_err, { + KeyType kk; + extract_key(*key, kk); + auto ret = this->get_entry(kk); + if(ret != nullptr) { + auto owned_ptr = owner->find_unset_accessed_table_entry(); + *owned_ptr = ret; + return static_cast(owned_ptr); + } + throw sinsp_exception("get_entry found no element at given key"); + return NULL; + }); + return NULL; +} + +template +void libsinsp::state::built_in_table::release_table_entry(sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e) { + static_cast*>(_e)->reset(); +} + +template +ss_plugin_bool libsinsp::state::built_in_table::iterate_entries( + sinsp_table_owner* owner, + ss_plugin_table_iterator_func_t it, + ss_plugin_table_iterator_state_t* s) { + std::shared_ptr owned_ptr; + std::function iter = [&owned_ptr, &it, &s](auto& e) { + owned_ptr.reset(&e, [](libsinsp::state::table_entry* p) {}); + return it(s, static_cast(&owned_ptr)) != 0; + }; + + __CATCH_ERR_MSG(owner->m_last_owner_err, { return this->foreach_entry(iter); }); + + return false; +} + +template +ss_plugin_rc libsinsp::state::built_in_table::clear_entries(sinsp_table_owner* owner) { + __CATCH_ERR_MSG(owner->m_last_owner_err, { + this->clear_entries(); + return SS_PLUGIN_SUCCESS; + }); + return SS_PLUGIN_FAILURE; +} + +template +ss_plugin_rc libsinsp::state::built_in_table::erase_entry( + sinsp_table_owner* owner, + const ss_plugin_state_data* key) { + __CATCH_ERR_MSG(owner->m_last_owner_err, { + KeyType kk; + extract_key(*key, kk); + if(this->erase_entry(kk)) { + return SS_PLUGIN_SUCCESS; + } + owner->m_last_owner_err = "table entry not found"; + return SS_PLUGIN_FAILURE; + }); + return SS_PLUGIN_FAILURE; +} + +template +ss_plugin_table_entry_t* libsinsp::state::built_in_table::create_table_entry( + sinsp_table_owner* owner) { + __CATCH_ERR_MSG(owner->m_last_owner_err, { + auto ret = this->new_entry().release(); + auto owned_ptr = owner->find_unset_accessed_table_entry(); + owned_ptr->reset(ret, [](libsinsp::state::table_entry* p) { /* do nothing */ }); + return static_cast(owned_ptr); + }); + return NULL; +} + +template +void libsinsp::state::built_in_table::destroy_table_entry(sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e) { + __CATCH_ERR_MSG(owner->m_last_owner_err, { + auto e = static_cast*>(_e); + auto ptr = std::unique_ptr(e->get()); + e->reset(); + }); +} + +template +ss_plugin_table_entry_t* libsinsp::state::built_in_table::add_entry( + sinsp_table_owner* owner, + const ss_plugin_state_data* key, + ss_plugin_table_entry_t* _e) { + __CATCH_ERR_MSG(owner->m_last_owner_err, { + KeyType kk; + extract_key(*key, kk); + auto e = static_cast*>(_e); + auto ptr = std::unique_ptr(e->get()); + e->reset(); + auto owned_ptr = owner->find_unset_accessed_table_entry(); + *owned_ptr = this->add_entry(kk, std::move(ptr)); + return static_cast(owned_ptr); + }); + return NULL; +} + +template +ss_plugin_rc libsinsp::state::built_in_table::read_entry_field( + sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out) { + auto a = static_cast(f); + auto e = static_cast*>(_e); + auto res = SS_PLUGIN_FAILURE; + +#define _X(_type, _dtype) \ + { \ + if(a->dynamic) { \ + auto aa = static_cast*>( \ + a->accessor); \ + e->get()->get_dynamic_field<_type>(*aa, out->_dtype); \ + } else { \ + auto aa = static_cast*>( \ + a->accessor); \ + e->get()->get_static_field<_type>(*aa, out->_dtype); \ + } \ + res = SS_PLUGIN_SUCCESS; \ + break; \ + } + __CATCH_ERR_MSG(owner->m_last_owner_err, { __PLUGIN_STATETYPE_SWITCH(a->data_type); }); +#undef _X + +#define _X(_type, _dtype) \ + { \ + auto st = static_cast*>(subtable_ptr); \ + auto& slot = owner->find_unset_ephemeral_table(); \ + slot.set<_type>(owner, st); \ + out->table = &slot.input; \ + }; + if(a->data_type == ss_plugin_state_type::SS_PLUGIN_ST_TABLE) { + auto* subtable_ptr = out->table; + __CATCH_ERR_MSG(owner->m_last_owner_err, + { __PLUGIN_STATETYPE_SWITCH(a->subtable_key_type); }); + } +#undef _X + + return res; +} + +template +ss_plugin_rc libsinsp::state::built_in_table::write_entry_field( + sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in) { + auto a = static_cast(f); + auto e = static_cast*>(_e); + + // todo(jasondellaluce): drop this check once we start supporting this + if(a->data_type == ss_plugin_state_type::SS_PLUGIN_ST_TABLE) { + owner->m_last_owner_err = "writing to table fields is currently not supported"; + return SS_PLUGIN_FAILURE; + } + +#define _X(_type, _dtype) \ + { \ + if(a->dynamic) { \ + auto aa = static_cast*>( \ + a->accessor); \ + _type val; \ + convert_types(in->_dtype, val); \ + e->get()->set_dynamic_field<_type>(*aa, val); \ + } else { \ + auto aa = static_cast*>( \ + a->accessor); \ + _type val; \ + convert_types(in->_dtype, val); \ + e->get()->set_static_field<_type>(*aa, val); \ + } \ + return SS_PLUGIN_SUCCESS; \ + } + __CATCH_ERR_MSG(owner->m_last_owner_err, { __PLUGIN_STATETYPE_SWITCH(a->data_type); }); +#undef _X + return SS_PLUGIN_FAILURE; +} + +template class libsinsp::state::built_in_table; +template class libsinsp::state::built_in_table; diff --git a/userspace/libsinsp/state/table.h b/userspace/libsinsp/state/table.h index dd7a3bc1ac..a463ce44bf 100644 --- a/userspace/libsinsp/state/table.h +++ b/userspace/libsinsp/state/table.h @@ -14,16 +14,39 @@ limitations under the License. #pragma once +#include #include #include #include +#include #include #include #include +#include namespace libsinsp { namespace state { +class sinsp_table_owner; + +// wraps instances of libsinsp::state::XXX_struct::field_accessor and +// help making them comply to the plugin API state tables definitions +struct sinsp_field_accessor_wrapper { + // depending on the value of `dynamic`, one of: + // - libsinsp::state::static_struct::field_accessor + // - libsinsp::state::dynamic_struct::field_accessor + void* accessor = nullptr; + bool dynamic = false; + ss_plugin_state_type data_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; + ss_plugin_state_type subtable_key_type = ss_plugin_state_type::SS_PLUGIN_ST_INT8; + + inline sinsp_field_accessor_wrapper() = default; + ~sinsp_field_accessor_wrapper(); + inline sinsp_field_accessor_wrapper(const sinsp_field_accessor_wrapper& s) = delete; + inline sinsp_field_accessor_wrapper& operator=(const sinsp_field_accessor_wrapper& s) = delete; + sinsp_field_accessor_wrapper(sinsp_field_accessor_wrapper&& s); + sinsp_field_accessor_wrapper& operator=(sinsp_field_accessor_wrapper&& s); +}; /** * @brief Base class for entries of a state table. @@ -39,20 +62,75 @@ struct table_entry : public static_struct, dynamic_struct { table_entry& operator=(const table_entry& s) = default; }; +template +class table; + +// wraps instances of libsinsp::state::table and help making them comply +// to the plugin API state tables definitions +struct table_accessor { + sinsp_table_owner* m_owner_plugin = nullptr; + libsinsp::state::base_table* m_table = nullptr; + + // plugin-defined vtables + ss_plugin_table_input input; + ss_plugin_table_fields_vtable_ext fields_vtable; + ss_plugin_table_reader_vtable_ext reader_vtable; + ss_plugin_table_writer_vtable_ext writer_vtable; + + table_accessor(); + virtual ~table_accessor() = default; + inline table_accessor(const table_accessor& s) = delete; + inline table_accessor& operator=(const table_accessor& s) = delete; + + void unset(); + bool is_set() const; + template + void set(sinsp_table_owner* p, libsinsp::state::table* t); + + // static functions, will be used to populate vtable functions where + // ss_plugin_table_t* will point to a `table_accessor` instance + static inline const ss_plugin_table_fieldinfo* list_fields(ss_plugin_table_t* _t, + uint32_t* nfields); + static inline ss_plugin_table_field_t* get_field(ss_plugin_table_t* _t, + const char* name, + ss_plugin_state_type data_type); + static inline ss_plugin_table_field_t* add_field(ss_plugin_table_t* _t, + const char* name, + ss_plugin_state_type data_type); + static inline const char* get_name(ss_plugin_table_t* _t); + static inline uint64_t get_size(ss_plugin_table_t* _t); + static inline ss_plugin_table_entry_t* get_entry(ss_plugin_table_t* _t, + const ss_plugin_state_data* key); + static inline ss_plugin_rc read_entry_field(ss_plugin_table_t* _t, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out); + ; + static inline void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e); + static inline ss_plugin_bool iterate_entries(ss_plugin_table_t* _t, + ss_plugin_table_iterator_func_t it, + ss_plugin_table_iterator_state_t* s); + static inline ss_plugin_rc clear(ss_plugin_table_t* _t); + static inline ss_plugin_rc erase_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key); + static inline ss_plugin_table_entry_t* create_table_entry(ss_plugin_table_t* _t); + static inline void destroy_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e); + static inline ss_plugin_table_entry_t* add_entry(ss_plugin_table_t* _t, + const ss_plugin_state_data* key, + ss_plugin_table_entry_t* _e); + static inline ss_plugin_rc write_entry_field(ss_plugin_table_t* _t, + ss_plugin_table_entry_t* e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in); + ; +}; + /** * @brief Base non-templated interface for state tables, defining * type-independent properties common to all tables. */ class base_table { public: - inline base_table(const std::string& name, - const typeinfo& key_info, - const static_struct::field_infos* static_fields): - m_this_ptr(this), - m_name(name), - m_key_info(key_info), - m_static_fields(static_fields), - m_dynamic_fields(std::make_shared()) {} + inline base_table(const typeinfo& key_info): m_key_info(key_info) {} virtual ~base_table() = default; inline base_table(base_table&&) = default; @@ -60,91 +138,62 @@ class base_table { inline base_table(const base_table& s) = delete; inline base_table& operator=(const base_table& s) = delete; - /** - * @brief Returns a pointer to the area of memory in which this table - * object is allocated. Here for convenience as required in other code parts. - */ - inline const base_table* const& table_ptr() const { return m_this_ptr; } - /** * @brief Returns the name of the table. */ - inline const std::string& name() const { return m_name; } + virtual const char* name() const = 0; /** * @brief Returns the non-null type info about the table's key. */ inline const typeinfo& key_info() const { return m_key_info; } - /** - * @brief Returns the fields metadata list for the static fields defined - * for the value data type of this table. This fields will be accessible - * for all the entries of this table. - */ - virtual const static_struct::field_infos* static_fields() const { return m_static_fields; } + virtual const ss_plugin_table_fieldinfo* list_fields(sinsp_table_owner* owner, + uint32_t* nfields) = 0; - /** - * @brief Returns the fields metadata list for the dynamic fields defined - * for the value data type of this table. This fields will be accessible - * for all the entries of this table. The returned metadata list can - * be expended at runtime by adding new dynamic fields, which will then - * be allocated and accessible for all the present and future entries - * present in the table. - */ - virtual const std::shared_ptr& dynamic_fields() const { - return m_dynamic_fields; - } + virtual ss_plugin_table_field_t* get_field(sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) = 0; - virtual void set_dynamic_fields(const std::shared_ptr& dynf) { - if(m_dynamic_fields.get() == dynf.get()) { - return; - } - if(!dynf) { - throw sinsp_exception("null definitions passed to set_dynamic_fields"); - } - if(m_dynamic_fields && m_dynamic_fields.use_count() > 1) { - throw sinsp_exception("can't replace already in-use dynamic fields table definitions"); - } - m_dynamic_fields = dynf; - } + virtual ss_plugin_table_field_t* add_field(sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) = 0; - /** - * @brief Returns the number of entries present in the table. - */ - virtual size_t entries_count() const = 0; + virtual uint64_t get_size(sinsp_table_owner* owner) = 0; - /** - * @brief Erase all the entries present in the table. - * After invoking this function, entries_count() will return true. - */ - virtual void clear_entries() = 0; + virtual ss_plugin_table_entry_t* get_entry(sinsp_table_owner* owner, + const ss_plugin_state_data* key) = 0; - /** - * @brief Allocates and returns a new entry for the table. This is just - * a factory method, the entry will not automatically added to the table. - * Once a new entry is allocated with this method, users must invoke - * add_entry() in order to actually insert it in the table. - */ - virtual std::unique_ptr new_entry() const = 0; + virtual void release_table_entry(sinsp_table_owner* owner, ss_plugin_table_entry_t* _e) = 0; - /** - * @brief Iterates over all the entries contained in the table and invokes - * the given predicate for each of them. - * - * @param pred The predicate to invoke for all the table's entries. The - * predicate returns true if the iteration can proceed to the next entry, - * and false if the iteration needs to break out. - * @return true If the iteration proceeded successfully for all the entries. - * @return false If the iteration broke out. - */ - virtual bool foreach_entry(std::function pred) = 0; + virtual ss_plugin_bool iterate_entries(sinsp_table_owner* owner, + ss_plugin_table_iterator_func_t it, + ss_plugin_table_iterator_state_t* s) = 0; -private: - const base_table* m_this_ptr; - std::string m_name; + virtual ss_plugin_rc clear_entries(sinsp_table_owner* owner) = 0; + + virtual ss_plugin_rc erase_entry(sinsp_table_owner* owner, const ss_plugin_state_data* key) = 0; + + virtual ss_plugin_table_entry_t* create_table_entry(sinsp_table_owner* owner) = 0; + + virtual void destroy_table_entry(sinsp_table_owner* owner, ss_plugin_table_entry_t* _e) = 0; + + virtual ss_plugin_table_entry_t* add_entry(sinsp_table_owner* owner, + const ss_plugin_state_data* key, + ss_plugin_table_entry_t* _e) = 0; + + virtual ss_plugin_rc read_entry_field(sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out) = 0; + + virtual ss_plugin_rc write_entry_field(sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in) = 0; + +protected: typeinfo m_key_info; - const static_struct::field_infos* m_static_fields; - std::shared_ptr m_dynamic_fields; }; /** @@ -156,14 +205,48 @@ class table : public base_table { "table key types must have a default constructor"); public: - inline table(const std::string& name, const static_struct::field_infos* static_fields): - base_table(name, typeinfo::of(), static_fields) {} - inline table(const std::string& name): table(name, _static_fields()) {} + inline table(): base_table(typeinfo::of()) {} virtual ~table() = default; inline table(table&&) = default; inline table& operator=(table&&) = default; inline table(const table& s) = delete; inline table& operator=(const table& s) = delete; +}; + +template +class built_in_table : public table { +public: + inline built_in_table(const std::string& name, const static_struct::field_infos* static_fields): + table::table(), + m_this_ptr(this), + m_name(name), + m_static_fields(static_fields), + m_dynamic_fields(std::make_shared()) {} + inline built_in_table(const std::string& name): + table::table(), + m_this_ptr(this), + m_name(name), + m_dynamic_fields(std::make_shared()) {} + + /** + * @brief Returns a pointer to the area of memory in which this table + * object is allocated. Here for convenience as required in other code parts. + */ + inline const base_table* const& table_ptr() const { return m_this_ptr; } + + const char* name() const override { return m_name.c_str(); } + + /** + * @brief Returns the fields metadata list for the static fields defined + * for the value data type of this table. This fields will be accessible + * for all the entries of this table. + */ + virtual const static_struct::field_infos* static_fields() const { return m_static_fields; } + + /** + * @brief Returns the number of entries present in the table. + */ + virtual size_t entries_count() const = 0; /** * @brief Returns a pointer to an entry present in the table at the given @@ -176,6 +259,14 @@ class table : public base_table { */ virtual std::shared_ptr get_entry(const KeyType& key) = 0; + /** + * @brief Allocates and returns a new entry for the table. This is just + * a factory method, the entry will not automatically added to the table. + * Once a new entry is allocated with this method, users must invoke + * add_entry() in order to actually insert it in the table. + */ + virtual std::unique_ptr new_entry() const = 0; + /** * @brief Inserts a new entry in the table with the given key. If another * entry is already present with the same key, it gets replaced. After @@ -190,6 +281,12 @@ class table : public base_table { virtual std::shared_ptr add_entry(const KeyType& key, std::unique_ptr entry) = 0; + /** + * @brief Erase all the entries present in the table. + * After invoking this function, entries_count() will return true. + */ + virtual void clear_entries() = 0; + /** * @brief Removes an entry from the table with the given key. * @@ -199,11 +296,166 @@ class table : public base_table { */ virtual bool erase_entry(const KeyType& key) = 0; + /** + * @brief Iterates over all the entries contained in the table and invokes + * the given predicate for each of them. + * + * @param pred The predicate to invoke for all the table's entries. The + * predicate returns true if the iteration can proceed to the next entry, + * and false if the iteration needs to break out. + * @return true If the iteration proceeded successfully for all the entries. + * @return false If the iteration broke out. + */ + virtual bool foreach_entry(std::function pred) = 0; + + /** + * @brief Returns the fields metadata list for the dynamic fields defined + * for the value data type of this table. This fields will be accessible + * for all the entries of this table. The returned metadata list can + * be expended at runtime by adding new dynamic fields, which will then + * be allocated and accessible for all the present and future entries + * present in the table. + */ + virtual const std::shared_ptr& dynamic_fields() const { + return m_dynamic_fields; + } + + virtual void set_dynamic_fields(const std::shared_ptr& dynf) { + if(m_dynamic_fields.get() == dynf.get()) { + return; + } + if(!dynf) { + throw sinsp_exception("null definitions passed to set_dynamic_fields"); + } + if(m_dynamic_fields && m_dynamic_fields.use_count() > 1) { + throw sinsp_exception("can't replace already in-use dynamic fields table definitions"); + } + m_dynamic_fields = dynf; + } + + uint64_t get_size(sinsp_table_owner* owner) override; + + const ss_plugin_table_fieldinfo* list_fields(sinsp_table_owner* owner, + uint32_t* nfields) override; + + ss_plugin_table_field_t* get_field(sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) override; + + ss_plugin_table_field_t* add_field(sinsp_table_owner* owner, + const char* name, + ss_plugin_state_type data_type) override; + + ss_plugin_table_entry_t* get_entry(sinsp_table_owner* owner, + const ss_plugin_state_data* key) override; + + void release_table_entry(sinsp_table_owner* owner, ss_plugin_table_entry_t* _e) override; + + ss_plugin_bool iterate_entries(sinsp_table_owner* owner, + ss_plugin_table_iterator_func_t it, + ss_plugin_table_iterator_state_t* s) override; + + ss_plugin_rc clear_entries(sinsp_table_owner* owner) override; + + ss_plugin_rc erase_entry(sinsp_table_owner* owner, const ss_plugin_state_data* key) override; + + ss_plugin_table_entry_t* create_table_entry(sinsp_table_owner* owner) override; + + void destroy_table_entry(sinsp_table_owner* owner, ss_plugin_table_entry_t* _e) override; + + ss_plugin_table_entry_t* add_entry(sinsp_table_owner* owner, + const ss_plugin_state_data* key, + ss_plugin_table_entry_t* _e) override; + + ss_plugin_rc read_entry_field(sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + ss_plugin_state_data* out) override; + + ss_plugin_rc write_entry_field(sinsp_table_owner* owner, + ss_plugin_table_entry_t* _e, + const ss_plugin_table_field_t* f, + const ss_plugin_state_data* in) override; + private: - static inline const static_struct::field_infos* _static_fields() { - static const static_struct::field_infos s_fields{}; - return &s_fields; + const base_table* m_this_ptr; + std::string m_name; + const static_struct::field_infos* m_static_fields; + std::vector m_field_list; + std::unordered_map m_field_accessors; + std::shared_ptr m_dynamic_fields; +}; + +class sinsp_table_owner { +public: + sinsp_table_owner() = default; + virtual ~sinsp_table_owner() = default; + + std::string m_last_owner_err; + +protected: + std::list> + m_accessed_entries; // using lists for ptr stability + std::list + m_ephemeral_tables; // note: lists have pointer stability + std::list + m_accessed_table_fields; // note: lists have pointer stability + + bool m_ephemeral_tables_clear = false; + bool m_accessed_entries_clear = false; + + inline void clear_ephemeral_tables() { + if(m_ephemeral_tables_clear) { + // quick break-out that prevents us from looping over the + // whole list in the critical path, in case of no accessed table + return; + } + for(auto& et : m_ephemeral_tables) { + et.unset(); + } + m_ephemeral_tables_clear = true; } + + inline void clear_accessed_entries() { + if(m_accessed_entries_clear) { + // quick break-out that prevents us from looping over the + // whole list in the critical path + return; + } + for(auto& et : m_accessed_entries) { + if(et != nullptr) { + // if we get here, it means that the plugin did not + // release some of the entries it acquired + ASSERT(false); + et.reset(); + }; + } + m_accessed_entries_clear = true; + } + +public: + inline libsinsp::state::table_accessor& find_unset_ephemeral_table() { + m_ephemeral_tables_clear = false; + for(auto& et : m_ephemeral_tables) { + if(!et.is_set()) { + return et; + } + } + return m_ephemeral_tables.emplace_back(); + } + + inline std::shared_ptr* find_unset_accessed_table_entry() { + m_accessed_entries_clear = false; + for(auto& et : m_accessed_entries) { + if(et == nullptr) { + return &et; + } + } + return &m_accessed_entries.emplace_back(); + } + + template + friend class libsinsp::state::built_in_table; }; }; // namespace state diff --git a/userspace/libsinsp/state/table_adapters.h b/userspace/libsinsp/state/table_adapters.h index ac281ab020..27fcaf2410 100644 --- a/userspace/libsinsp/state/table_adapters.h +++ b/userspace/libsinsp/state/table_adapters.h @@ -116,7 +116,7 @@ class pair_table_entry_adapter : public libsinsp::state::table_entry { template 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) { + if(i.info().type_id() == SS_PLUGIN_ST_STRING) { *((const char**)out) = ((const std::string*)value)->c_str(); } else { memcpy(out, (const void*)value, i.info().size()); @@ -125,7 +125,7 @@ class pair_table_entry_adapter : public libsinsp::state::table_entry { template 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) { + if(i.info().type_id() == SS_PLUGIN_ST_STRING) { *((std::string*)value) = *((const char**)in); } else { memcpy((void*)value, in, i.info().size()); @@ -175,7 +175,7 @@ class value_table_entry_adapter : public libsinsp::state::table_entry { "invalid field info passed to value_table_entry_adapter::get_dynamic_field"); } - if(i.info().index() == typeinfo::index_t::TI_STRING) { + if(i.info().type_id() == SS_PLUGIN_ST_STRING) { *((const char**)out) = ((const std::string*)m_value)->c_str(); } else { memcpy(out, (const void*)m_value, i.info().size()); @@ -189,7 +189,7 @@ class value_table_entry_adapter : public libsinsp::state::table_entry { "invalid field info passed to value_table_entry_adapter::set_dynamic_field"); } - if(i.info().index() == typeinfo::index_t::TI_STRING) { + if(i.info().type_id() == SS_PLUGIN_ST_STRING) { *((std::string*)m_value) = *((const char**)in); } else { memcpy((void*)m_value, in, i.info().size()); @@ -217,10 +217,10 @@ class value_table_entry_adapter : public libsinsp::state::table_entry { template, typename DynFields = typename TWrap::dynamic_fields_t> -class stl_container_table_adapter : public libsinsp::state::table { +class stl_container_table_adapter : public libsinsp::state::built_in_table { public: stl_container_table_adapter(const std::string& name, T& container): - table(name, _static_fields()), + built_in_table(name, _static_fields()), m_container(container) { set_dynamic_fields(std::make_shared()); } @@ -260,20 +260,22 @@ class stl_container_table_adapter : public libsinsp::state::table { const uint64_t& key, std::unique_ptr entry) override { if(!entry) { - throw sinsp_exception("null entry added to table: " + this->name()); + throw sinsp_exception( + std::string("null entry added to table: " + std::string(this->name()))); } if(entry->dynamic_fields() != this->dynamic_fields()) { throw sinsp_exception("entry with mismatching dynamic fields added to table: " + - this->name()); + std::string(this->name())); } auto value = dynamic_cast(entry.get()); if(!value) { - throw sinsp_exception("entry with mismatching type added to table: " + this->name()); + throw sinsp_exception("entry with mismatching type added to table: " + + std::string(this->name())); } if(value->value() != nullptr) { throw sinsp_exception("entry with unexpected owned value added to table: " + - this->name()); + std::string(this->name())); } m_container.resize(key + 1); diff --git a/userspace/libsinsp/state/table_registry.h b/userspace/libsinsp/state/table_registry.h index 0a9b6f5664..4527904bc6 100644 --- a/userspace/libsinsp/state/table_registry.h +++ b/userspace/libsinsp/state/table_registry.h @@ -86,11 +86,12 @@ class table_registry { if(!t) { throw sinsp_exception("null table added to registry"); } - const auto& it = m_tables.find(t->name()); + std::string name = t->name(); + const auto& it = m_tables.find(name); if(it != m_tables.end()) { - throw sinsp_exception("table added to registry multiple times: " + t->name()); + throw sinsp_exception("table added to registry multiple times: " + name); } - m_tables.insert({t->name(), t}); + m_tables.insert({name, t}); return t; } diff --git a/userspace/libsinsp/state/type_info.h b/userspace/libsinsp/state/type_info.h index 80c87945ad..f51fb75fb7 100644 --- a/userspace/libsinsp/state/type_info.h +++ b/userspace/libsinsp/state/type_info.h @@ -18,10 +18,9 @@ limitations under the License. #pragma once #include -#include +#include #include -#include namespace libsinsp { namespace state { @@ -36,24 +35,6 @@ namespace state { */ class typeinfo { public: - /** - * @brief Numeric identifier of a supported type. - */ - enum index_t : uint8_t { - TI_INT8 = 1, - TI_INT16 = 2, - TI_INT32 = 3, - TI_INT64 = 4, - TI_UINT8 = 5, - TI_UINT16 = 6, - TI_UINT32 = 7, - TI_UINT64 = 8, - TI_STRING = 9, - TI_TABLE = 10, - // note(jasondellaluce): weird value due to plugin API backward compatibility - TI_BOOL = 25, - }; - /** * @brief Returns a type info for the type T. */ @@ -71,11 +52,11 @@ class typeinfo { inline typeinfo& operator=(const typeinfo& s) = default; friend inline bool operator==(const typeinfo& a, const typeinfo& b) { - return a.index() == b.index(); + return a.type_id() == b.type_id(); }; friend inline bool operator!=(const typeinfo& a, const typeinfo& b) { - return a.index() != b.index(); + return a.type_id() != b.type_id(); }; /** @@ -86,7 +67,7 @@ class typeinfo { /** * @brief Returns the numeric representation of the type. */ - inline index_t index() const { return m_index; } + inline ss_plugin_state_type type_id() const { return m_type_id; } /** * @brief Returns the byte size of variables of the given type. @@ -112,9 +93,13 @@ class typeinfo { } private: - inline typeinfo(const char* n, index_t k, size_t s, void (*c)(void*), void (*d)(void*)): + inline typeinfo(const char* n, + ss_plugin_state_type k, + size_t s, + void (*c)(void*), + void (*d)(void*)): m_name(n), - m_index(k), + m_type_id(k), m_size(s), m_construct(c), m_destroy(d) {} @@ -132,12 +117,12 @@ class typeinfo { } template - static inline typeinfo _build(const char* n, index_t k) { + static inline typeinfo _build(const char* n, ss_plugin_state_type k) { return typeinfo(n, k, sizeof(T), _construct, _destroy); } const char* m_name; - index_t m_index; + ss_plugin_state_type m_type_id; size_t m_size; void (*m_construct)(void*); void (*m_destroy)(void*); @@ -148,51 +133,51 @@ class base_table; // below is the manually-controlled list of all the supported types template<> inline typeinfo typeinfo::of() { - return _build("bool", TI_BOOL); + return _build("bool", SS_PLUGIN_ST_BOOL); } template<> inline typeinfo typeinfo::of() { - return _build("int8", TI_INT8); + return _build("int8", SS_PLUGIN_ST_INT8); } template<> inline typeinfo typeinfo::of() { - return _build("int16", TI_INT16); + return _build("int16", SS_PLUGIN_ST_INT16); } template<> inline typeinfo typeinfo::of() { - return _build("int32", TI_INT32); + return _build("int32", SS_PLUGIN_ST_INT32); } template<> inline typeinfo typeinfo::of() { - return _build("int64", TI_INT64); + return _build("int64", SS_PLUGIN_ST_INT64); } template<> inline typeinfo typeinfo::of() { - return _build("uint8", TI_UINT8); + return _build("uint8", SS_PLUGIN_ST_UINT8); } template<> inline typeinfo typeinfo::of() { - return _build("uint16", TI_UINT16); + return _build("uint16", SS_PLUGIN_ST_UINT16); } template<> inline typeinfo typeinfo::of() { - return _build("uint32", TI_UINT32); + return _build("uint32", SS_PLUGIN_ST_UINT32); } template<> inline typeinfo typeinfo::of() { - return _build("uint64", TI_UINT64); + return _build("uint64", SS_PLUGIN_ST_UINT64); } template<> inline typeinfo typeinfo::of() { - return _build("string", TI_STRING); + return _build("string", SS_PLUGIN_ST_STRING); } template<> inline typeinfo typeinfo::of() { - return _build("table", TI_TABLE); + return _build("table", SS_PLUGIN_ST_TABLE); } template<> inline typeinfo typeinfo::of() { - return _build("table", TI_TABLE); + return _build("table", SS_PLUGIN_ST_TABLE); } }; // namespace state diff --git a/userspace/libsinsp/test/plugins.ut.cpp b/userspace/libsinsp/test/plugins.ut.cpp index 06a4abde57..0384ef14f3 100644 --- a/userspace/libsinsp/test/plugins.ut.cpp +++ b/userspace/libsinsp/test/plugins.ut.cpp @@ -702,6 +702,7 @@ TEST_F(sinsp_with_test_input, plugin_syscall_async) { // Basically, we are verifying that the sinsp <-> plugin tables access // is bidirectional and consistent. TEST_F(sinsp_with_test_input, plugin_tables) { + libsinsp::state::sinsp_table_owner owner; auto& reg = m_inspector.get_table_registry(); add_default_init_thread(); @@ -720,32 +721,25 @@ TEST_F(sinsp_with_test_input, plugin_tables) { ASSERT_NE(reg->get_table("plugin_sample"), nullptr); // get the plugin table and check its fields and info - auto table = reg->get_table("plugin_sample"); - ASSERT_EQ(table->name(), "plugin_sample"); + auto table_wrapper = sinsp_table(&owner, reg->get_table("plugin_sample")); + auto table = &table_wrapper; + ASSERT_EQ(table->name(), std::string("plugin_sample")); ASSERT_EQ(table->entries_count(), 0); ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); - ASSERT_EQ(table->static_fields()->size(), 0); - ASSERT_EQ(table->dynamic_fields()->fields().size(), 1); + ASSERT_EQ(table->fields().size(), 1); // get an already existing field form the plugin table - auto sfield = table->dynamic_fields()->fields().find("u64_val"); - ASSERT_NE(sfield, table->dynamic_fields()->fields().end()); - ASSERT_EQ(sfield->second.readonly(), false); - ASSERT_EQ(sfield->second.valid(), true); - ASSERT_EQ(sfield->second.index(), 0); - ASSERT_EQ(sfield->second.name(), "u64_val"); - ASSERT_EQ(sfield->second.info(), libsinsp::state::typeinfo::of()); + auto field_info = table->get_field_info("u64_val"); + ASSERT_NE(field_info, nullptr); + ASSERT_EQ(field_info->read_only, false); + ASSERT_EQ(field_info->field_type, ss_plugin_state_type::SS_PLUGIN_ST_UINT64); // add a new field in the plugin table - const auto& dfield = table->dynamic_fields()->add_field("str_val"); - ASSERT_NE(table->dynamic_fields()->fields().find("str_val"), - table->dynamic_fields()->fields().end()); - ASSERT_EQ(dfield, table->dynamic_fields()->fields().find("str_val")->second); - ASSERT_EQ(dfield.readonly(), false); - ASSERT_EQ(dfield.valid(), true); - ASSERT_EQ(dfield.index(), 1); - ASSERT_EQ(dfield.name(), "str_val"); - ASSERT_EQ(dfield.info(), libsinsp::state::typeinfo::of()); + table->add_field("str_val"); + field_info = table->get_field_info("str_val"); + ASSERT_NE(field_info, nullptr); + ASSERT_EQ(field_info->read_only, false); + ASSERT_EQ(field_info->field_type, ss_plugin_state_type::SS_PLUGIN_ST_STRING); // we open a capture and iterate, so that we make sure that all // the state operations keep working at every round of the loop @@ -767,71 +761,70 @@ TEST_F(sinsp_with_test_input, plugin_tables) { } // we play around with the plugin's table, like if it was a C++ one from sinsp - auto sfieldacc = sfield->second.new_accessor(); - auto dfieldacc = dfield.new_accessor(); + auto sfieldacc = table->get_field("u64_val"); + auto dfieldacc = table->get_field("str_val"); for(uint64_t i = 0; i < max_iterations; i++) { ASSERT_EQ(table->entries_count(), i); // get non-existing entry - ASSERT_EQ(table->get_entry(i), nullptr); + ASSERT_ANY_THROW(table->get_entry(i)); // creating a destroying a thread without adding it to the table table->new_entry(); // creating and adding a thread to the table - auto t = table->add_entry(i, table->new_entry()); - ASSERT_NE(t, nullptr); - ASSERT_NE(table->get_entry(i), nullptr); + auto e = table->new_entry(); + table->add_entry(i, e); + table->get_entry(i); ASSERT_EQ(table->entries_count(), i + 1); // read and write from newly-created thread (existing field) uint64_t tmpu64 = (uint64_t)-1; - t->get_dynamic_field(sfieldacc, tmpu64); + e.read_field(sfieldacc, tmpu64); ASSERT_EQ(tmpu64, 0); tmpu64 = 5; - t->set_dynamic_field(sfieldacc, tmpu64); + e.write_field(sfieldacc, tmpu64); tmpu64 = 0; - t->get_dynamic_field(sfieldacc, tmpu64); + e.read_field(sfieldacc, tmpu64); ASSERT_EQ(tmpu64, 5); // read and write from newly-created thread (added field) std::string tmpstr = "test"; - t->get_dynamic_field(dfieldacc, tmpstr); + e.read_field(dfieldacc, tmpstr); ASSERT_EQ(tmpstr, ""); tmpstr = "hello"; - t->set_dynamic_field(dfieldacc, tmpstr); + e.write_field(dfieldacc, tmpstr); tmpstr = ""; - t->get_dynamic_field(dfieldacc, tmpstr); + e.read_field(dfieldacc, tmpstr); ASSERT_EQ(tmpstr, "hello"); } // full iteration - auto it = [&](libsinsp::state::table_entry& e) -> bool { + auto it = [&](sinsp_table_entry& e) -> bool { uint64_t tmpu64; std::string tmpstr; - e.get_dynamic_field(sfieldacc, tmpu64); + e.read_field(sfieldacc, tmpu64); EXPECT_EQ(tmpu64, 5); - e.get_dynamic_field(dfieldacc, tmpstr); + e.read_field(dfieldacc, tmpstr); EXPECT_EQ(tmpstr, "hello"); return true; }; ASSERT_TRUE(table->foreach_entry(it)); // iteration with break-out - ASSERT_FALSE( - table->foreach_entry([&](libsinsp::state::table_entry& e) -> bool { return false; })); + ASSERT_FALSE(table->foreach_entry([&](sinsp_table_entry& e) -> bool { return false; })); // iteration with error ASSERT_ANY_THROW(table->foreach_entry( - [&](libsinsp::state::table_entry& e) -> bool { throw sinsp_exception("some error"); })); + [&](sinsp_table_entry& e) -> bool { throw sinsp_exception("some error"); })); // erasing an unknown thread - ASSERT_EQ(table->erase_entry(max_iterations), false); + ASSERT_ANY_THROW(table->erase_entry(max_iterations)); ASSERT_EQ(table->entries_count(), max_iterations); // erase one of the newly-created thread - ASSERT_EQ(table->erase_entry(0), true); + table->erase_entry(0); ASSERT_EQ(table->entries_count(), max_iterations - 1); // clear all @@ -846,9 +839,10 @@ TEST_F(sinsp_with_test_input, plugin_subtables) { register_plugin(&m_inspector, get_plugin_api_sample_syscall_subtables); - auto table = reg->get_table("threads"); + auto table = dynamic_cast*>( + reg->get_table("threads")); ASSERT_NE(table, nullptr); - ASSERT_EQ(table->name(), "threads"); + ASSERT_EQ(table->name(), std::string("threads")); ASSERT_EQ(table->entries_count(), 0); ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); ASSERT_EQ(table->dynamic_fields()->fields().size(), 0); @@ -872,7 +866,7 @@ TEST_F(sinsp_with_test_input, plugin_subtables) { auto subtable_acc = field->second.new_accessor(); auto subtable = dynamic_cast(entry->get_static_field(subtable_acc)); ASSERT_NE(subtable, nullptr); - ASSERT_EQ(subtable->name(), "file_descriptors"); + ASSERT_EQ(subtable->name(), std::string("file_descriptors")); ASSERT_EQ(subtable->entries_count(), 0); // get an accessor to one of the static fields @@ -944,9 +938,10 @@ TEST_F(sinsp_with_test_input, plugin_subtables_array) { register_plugin(&m_inspector, get_plugin_api_sample_syscall_subtables_array); - auto table = reg->get_table("threads"); + auto table = dynamic_cast*>( + reg->get_table("threads")); ASSERT_NE(table, nullptr); - ASSERT_EQ(table->name(), "threads"); + ASSERT_EQ(table->name(), std::string("threads")); ASSERT_EQ(table->entries_count(), 0); ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); ASSERT_EQ(table->dynamic_fields()->fields().size(), 0); @@ -972,7 +967,7 @@ TEST_F(sinsp_with_test_input, plugin_subtables_array) { dynamic_cast>*>( entry->get_static_field(subtable_acc)); ASSERT_NE(subtable, nullptr); - ASSERT_EQ(subtable->name(), "env"); + ASSERT_EQ(subtable->name(), std::string("env")); ASSERT_EQ(subtable->entries_count(), 0); // get an accessor to a dynamic field representing the array's values @@ -1036,9 +1031,10 @@ TEST_F(sinsp_with_test_input, plugin_subtables_array_pair) { register_plugin(&m_inspector, get_plugin_api_sample_syscall_subtables_array_pair); - auto table = reg->get_table("threads"); + auto table = dynamic_cast*>( + reg->get_table("threads")); ASSERT_NE(table, nullptr); - ASSERT_EQ(table->name(), "threads"); + ASSERT_EQ(table->name(), std::string("threads")); ASSERT_EQ(table->entries_count(), 0); ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); ASSERT_EQ(table->dynamic_fields()->fields().size(), 0); @@ -1066,7 +1062,7 @@ TEST_F(sinsp_with_test_input, plugin_subtables_array_pair) { libsinsp::state::pair_table_entry_adapter>*>( entry->get_static_field(subtable_acc)); ASSERT_NE(subtable, nullptr); - ASSERT_EQ(subtable->name(), "cgroups"); + ASSERT_EQ(subtable->name(), std::string("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 diff --git a/userspace/libsinsp/test/state.ut.cpp b/userspace/libsinsp/test/state.ut.cpp index 7b95e97dd2..3faabede1f 100644 --- a/userspace/libsinsp/test/state.ut.cpp +++ b/userspace/libsinsp/test/state.ut.cpp @@ -292,9 +292,9 @@ TEST(dynamic_struct, mem_ownership) { } TEST(table_registry, defs_and_access) { - class sample_table : public libsinsp::state::table { + class sample_table : public libsinsp::state::built_in_table { public: - sample_table(): table("sample") {} + sample_table(): built_in_table("sample") {} size_t entries_count() const override { return m_entries.size(); } @@ -355,10 +355,11 @@ TEST(thread_manager, table_access) { static const int s_threadinfo_static_fields_count = 33; sinsp inspector; - auto table = static_cast*>(inspector.m_thread_manager.get()); + auto table = static_cast*>( + inspector.m_thread_manager.get()); // empty table state and info - ASSERT_EQ(table->name(), "threads"); + ASSERT_EQ(table->name(), std::string("threads")); ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); ASSERT_EQ(*table->static_fields(), sinsp_threadinfo().static_fields()); ASSERT_NE(table->dynamic_fields(), nullptr); @@ -384,7 +385,7 @@ TEST(thread_manager, table_access) { ASSERT_EQ(newt->get_static_field(tid_acc), (int64_t)999); ASSERT_EQ(newt->get_static_field(comm_acc), "test"); ASSERT_NE(newt->get_static_field(fdtable_acc), nullptr); - ASSERT_EQ(newt->get_static_field(fdtable_acc)->name(), "file_descriptors"); + ASSERT_EQ(newt->get_static_field(fdtable_acc)->name(), std::string("file_descriptors")); ASSERT_NO_THROW(table->add_entry(999, std::move(newt))); ASSERT_EQ(table->entries_count(), 1); auto addedt = table->get_entry(999); @@ -392,7 +393,7 @@ TEST(thread_manager, table_access) { ASSERT_EQ(addedt->get_static_field(tid_acc), (int64_t)999); ASSERT_EQ(addedt->get_static_field(comm_acc), "test"); ASSERT_NE(addedt->get_static_field(fdtable_acc), nullptr); - ASSERT_EQ(addedt->get_static_field(fdtable_acc)->name(), "file_descriptors"); + ASSERT_EQ(addedt->get_static_field(fdtable_acc)->name(), std::string("file_descriptors")); // add a dynamic field to table std::string tmpstr; @@ -448,8 +449,9 @@ TEST(thread_manager, fdtable_access) { ASSERT_EQ(reg->tables().size(), 1); ASSERT_NE(reg->tables().find("threads"), reg->tables().end()); - auto table = reg->get_table("threads"); - ASSERT_EQ(table->name(), "threads"); + auto table = dynamic_cast*>( + reg->get_table("threads")); + ASSERT_EQ(table->name(), std::string("threads")); ASSERT_EQ(table->entries_count(), 0); ASSERT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); ASSERT_EQ(table->dynamic_fields()->fields().size(), 0); @@ -482,7 +484,7 @@ TEST(thread_manager, fdtable_access) { ASSERT_NE(subtable, nullptr); ASSERT_NE(subtable2, nullptr); - ASSERT_EQ(subtable->name(), "file_descriptors"); + ASSERT_EQ(subtable->name(), std::string("file_descriptors")); ASSERT_EQ(subtable->entries_count(), 0); ASSERT_EQ(subtable->key_info(), libsinsp::state::typeinfo::of()); ASSERT_EQ(subtable->static_fields()->size(), s_fdinfo_static_fields_count); @@ -593,8 +595,9 @@ TEST(thread_manager, env_vars_access) { ASSERT_EQ(reg->tables().size(), 1); ASSERT_NE(reg->tables().find("threads"), reg->tables().end()); - auto table = reg->get_table("threads"); - EXPECT_EQ(table->name(), "threads"); + auto table = dynamic_cast*>( + reg->get_table("threads")); + EXPECT_EQ(table->name(), std::string("threads")); EXPECT_EQ(table->entries_count(), 0); EXPECT_EQ(table->key_info(), libsinsp::state::typeinfo::of()); EXPECT_EQ(table->dynamic_fields()->fields().size(), 0); @@ -620,7 +623,7 @@ TEST(thread_manager, env_vars_access) { dynamic_cast>*>( entry->get_static_field(subtable_acc)); ASSERT_NE(subtable, nullptr); - EXPECT_EQ(subtable->name(), "env"); + EXPECT_EQ(subtable->name(), std::string("env")); EXPECT_EQ(subtable->entries_count(), 0); EXPECT_EQ(subtable->key_info(), libsinsp::state::typeinfo::of()); EXPECT_EQ(subtable->static_fields()->size(), 0); diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index 274eb9775b..cf0d556ff0 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -1341,7 +1341,7 @@ static const auto s_threadinfo_static_fields = sinsp_threadinfo().static_fields( // sinsp_thread_manager implementation /////////////////////////////////////////////////////////////////////////////// sinsp_thread_manager::sinsp_thread_manager(sinsp* inspector): - table(s_thread_table_name, &s_threadinfo_static_fields), + built_in_table(s_thread_table_name, &s_threadinfo_static_fields), m_max_thread_table_size(m_thread_table_default_size), m_fdtable_dyn_fields(std::make_shared()) { m_inspector = inspector; diff --git a/userspace/libsinsp/threadinfo.h b/userspace/libsinsp/threadinfo.h index aed22fff92..37c54bb1bd 100644 --- a/userspace/libsinsp/threadinfo.h +++ b/userspace/libsinsp/threadinfo.h @@ -704,7 +704,7 @@ class threadinfo_map_t { /////////////////////////////////////////////////////////////////////////////// // This class manages the thread table /////////////////////////////////////////////////////////////////////////////// -class SINSP_PUBLIC sinsp_thread_manager : public libsinsp::state::table { +class SINSP_PUBLIC sinsp_thread_manager : public libsinsp::state::built_in_table { public: sinsp_thread_manager(sinsp* inspector); void clear();