From 43beb09be6c68dd9ff9fd5489aa29d2500f522ae Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 3 May 2024 15:26:05 +0000 Subject: [PATCH] refactor(userspace/libsinsp): support subtable state field type and adapt to new plugin API Co-authored-by: Gianmatteo Palmieri Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin.cpp | 32 +- userspace/libsinsp/plugin.h | 186 +++- userspace/libsinsp/plugin_table_api.cpp | 1075 +++++++++++--------- userspace/libsinsp/state/dynamic_struct.h | 13 +- userspace/libsinsp/state/type_info.h | 43 +- userspace/libsinsp/test/plugins/tables.cpp | 11 +- 6 files changed, 846 insertions(+), 514 deletions(-) diff --git a/userspace/libsinsp/plugin.cpp b/userspace/libsinsp/plugin.cpp index 0f2ff4041f..d9ca41b655 100755 --- a/userspace/libsinsp/plugin.cpp +++ b/userspace/libsinsp/plugin.cpp @@ -179,11 +179,19 @@ bool sinsp_plugin::init(const std::string &config, std::string &errstr) ss_plugin_init_tables_input tables_in = {}; ss_plugin_table_fields_vtable_ext table_fields_ext = {}; + ss_plugin_table_reader_vtable reader_deprecated = {}; // unused + ss_plugin_table_reader_vtable_ext table_reader_ext = {}; + ss_plugin_table_writer_vtable writer_deprecated = {}; // unused + ss_plugin_table_writer_vtable_ext table_writer_ext = {}; if (m_caps & (CAP_PARSING | CAP_EXTRACTION)) { tables_in.fields_ext = &table_fields_ext; + tables_in.reader_ext = &table_reader_ext; + tables_in.writer_ext = &table_writer_ext; sinsp_plugin::table_field_api(tables_in.fields, table_fields_ext); + sinsp_plugin::table_read_api(reader_deprecated, table_reader_ext); + sinsp_plugin::table_write_api(writer_deprecated, table_writer_ext); tables_in.list_tables = sinsp_plugin::table_api_list_tables; tables_in.get_table = sinsp_plugin::table_api_get_table; tables_in.add_table = sinsp_plugin::table_api_add_table; @@ -219,6 +227,10 @@ bool sinsp_plugin::init(const std::string &config, std::string &errstr) m_parse_event_sources, m_parse_event_codes); } + // do some defensive garbage collection + clear_ephemeral_tables(); + clear_accessed_entries(); + return true; } @@ -883,7 +895,7 @@ std::unique_ptr sinsp_plugin::new_filtercheck(std::shared_pt return std::make_unique(plugin); } -bool sinsp_plugin::extract_fields(sinsp_evt* evt, uint32_t num_fields, ss_plugin_extract_field *fields) const +bool sinsp_plugin::extract_fields(sinsp_evt* evt, uint32_t num_fields, ss_plugin_extract_field *fields) { if (!m_inited) { @@ -903,14 +915,20 @@ bool sinsp_plugin::extract_fields(sinsp_evt* evt, uint32_t num_fields, ss_plugin in.get_owner_last_error = sinsp_plugin::get_owner_last_error; in.table_reader_ext = &table_reader_ext; sinsp_plugin::table_read_api(in.table_reader, table_reader_ext); - return m_handle->api.extract_fields(m_state, &ev, &in) == SS_PLUGIN_SUCCESS; + auto res = m_handle->api.extract_fields(m_state, &ev, &in) == SS_PLUGIN_SUCCESS; + + // do some defensive garbage collection + clear_ephemeral_tables(); + clear_accessed_entries(); + + return res; } /** End of Field Extraction CAP **/ /** Event Parsing CAP **/ -bool sinsp_plugin::parse_event(sinsp_evt* evt) const +bool sinsp_plugin::parse_event(sinsp_evt* evt) { if (!m_inited) { @@ -931,9 +949,13 @@ bool sinsp_plugin::parse_event(sinsp_evt* evt) const in.table_writer_ext = &table_writer_ext; sinsp_plugin::table_read_api(in.table_reader, table_reader_ext); sinsp_plugin::table_write_api(in.table_writer, table_writer_ext); + auto res = m_handle->api.parse_event(m_state, &ev, &in) == SS_PLUGIN_SUCCESS; + + // do some defensive garbage collection + clear_ephemeral_tables(); + clear_accessed_entries(); - auto res = m_handle->api.parse_event(m_state, &ev, &in); - return res == SS_PLUGIN_SUCCESS; + return res; } /** End of Event Parsing CAP **/ diff --git a/userspace/libsinsp/plugin.h b/userspace/libsinsp/plugin.h index 7d4fdab9a2..cc93ccb624 100755 --- a/userspace/libsinsp/plugin.h +++ b/userspace/libsinsp/plugin.h @@ -113,13 +113,18 @@ class sinsp_plugin m_extract_event_codes(), m_parse_event_sources(), m_parse_event_codes(), + m_async_event_sources(), + m_async_event_names(), + m_async_evt_handler(nullptr), m_table_registry(treg), m_table_infos(), m_owned_tables(), m_accessed_tables(), - m_async_event_sources(), - m_async_event_names(), - m_async_evt_handler(nullptr) { } + m_accessed_entries(), + m_accessed_table_fields(), + m_ephemeral_tables(), + m_ephemeral_tables_clear(false), + m_accessed_entries_clear(false) { } virtual ~sinsp_plugin(); sinsp_plugin(const sinsp_plugin& s) = delete; sinsp_plugin& operator = (const sinsp_plugin& s) = delete; @@ -195,7 +200,7 @@ class sinsp_plugin return m_fields; } - bool extract_fields(sinsp_evt* evt, uint32_t num_fields, ss_plugin_extract_field *fields) const; + bool extract_fields(sinsp_evt* evt, uint32_t num_fields, ss_plugin_extract_field *fields); /** Event Parsing **/ inline const std::unordered_set& parse_event_sources() const @@ -205,7 +210,7 @@ class sinsp_plugin const libsinsp::events::set& parse_event_codes() const; - bool parse_event(sinsp_evt* evt) const; + bool parse_event(sinsp_evt* evt); /** Async Events **/ inline const std::unordered_set& async_event_sources() const @@ -254,21 +259,14 @@ class sinsp_plugin libsinsp::events::set m_extract_event_codes; /** Event Parsing **/ - struct accessed_table_input_deleter { void operator()(ss_plugin_table_input* r); }; - using owned_table_t = std::unique_ptr; - using accessed_table_t = std::unique_ptr; std::unordered_set m_parse_event_sources; libsinsp::events::set m_parse_event_codes; - 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; - /** Async Events **/ + /** Async Events state and helpers **/ std::unordered_set m_async_event_sources; std::unordered_set m_async_event_names; std::atomic m_async_evt_handler; // note: we don't have thread-safe smart pointers + static ss_plugin_rc handle_plugin_async_event(ss_plugin_owner_t *o, const ss_plugin_event* evt, char* err); /** Generic helpers **/ void validate_config(std::string& config); @@ -285,7 +283,162 @@ class sinsp_plugin void validate_config_json_schema(std::string& config, std::string& schema); static const char* get_owner_last_error(ss_plugin_owner_t* o); - /** Event parsing helpers **/ + /** 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 + return; + } + for (auto& et : m_ephemeral_tables) + { + et.wrapper.unset(); + et.update(); + } + 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; + } + } + return m_ephemeral_tables.emplace_back(); + } + + 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; + } + + 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(); + } + 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); @@ -293,8 +446,5 @@ class sinsp_plugin 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); - /** Async events helpers **/ - static ss_plugin_rc handle_plugin_async_event(ss_plugin_owner_t *o, const ss_plugin_event* evt, char* err); - friend struct sinsp_table_wrapper; }; diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index e33662cd70..ce0ea573c5 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -49,6 +49,8 @@ limitations under the License. _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)); \ } \ @@ -58,28 +60,30 @@ static inline ss_plugin_state_type typeinfo_to_state_type(const libsinsp::state: { switch(i.index()) { - case libsinsp::state::typeinfo::index_t::PT_INT8: + case libsinsp::state::typeinfo::index_t::TI_INT8: return ss_plugin_state_type::SS_PLUGIN_ST_INT8; - case libsinsp::state::typeinfo::index_t::PT_INT16: + case libsinsp::state::typeinfo::index_t::TI_INT16: return ss_plugin_state_type::SS_PLUGIN_ST_INT16; - case libsinsp::state::typeinfo::index_t::PT_INT32: + case libsinsp::state::typeinfo::index_t::TI_INT32: return ss_plugin_state_type::SS_PLUGIN_ST_INT32; - case libsinsp::state::typeinfo::index_t::PT_INT64: + case libsinsp::state::typeinfo::index_t::TI_INT64: return ss_plugin_state_type::SS_PLUGIN_ST_INT64; - case libsinsp::state::typeinfo::index_t::PT_UINT8: + case libsinsp::state::typeinfo::index_t::TI_UINT8: return ss_plugin_state_type::SS_PLUGIN_ST_UINT8; - case libsinsp::state::typeinfo::index_t::PT_UINT16: + case libsinsp::state::typeinfo::index_t::TI_UINT16: return ss_plugin_state_type::SS_PLUGIN_ST_UINT16; - case libsinsp::state::typeinfo::index_t::PT_UINT32: + case libsinsp::state::typeinfo::index_t::TI_UINT32: return ss_plugin_state_type::SS_PLUGIN_ST_UINT32; - case libsinsp::state::typeinfo::index_t::PT_UINT64: + case libsinsp::state::typeinfo::index_t::TI_UINT64: return ss_plugin_state_type::SS_PLUGIN_ST_UINT64; - case libsinsp::state::typeinfo::index_t::PT_CHARBUF: + case libsinsp::state::typeinfo::index_t::TI_STRING: return ss_plugin_state_type::SS_PLUGIN_ST_STRING; - case libsinsp::state::typeinfo::index_t::PT_BOOL: + 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::string(i.name())); + throw sinsp_exception("can't convert typeinfo to plugin state type: " + std::to_string(i.index())); } } @@ -94,6 +98,16 @@ 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); +} + static void noop_release_table_entry(ss_plugin_table_t*, ss_plugin_table_entry_t*) { } @@ -385,6 +399,10 @@ struct plugin_table_wrapper: public libsinsp::state::table 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); @@ -398,7 +416,7 @@ struct plugin_table_wrapper: public libsinsp::state::table // 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::PT_CHARBUF) + if (i.info().index() == libsinsp::state::typeinfo::index_t::TI_STRING) { *(const char**) out = dout.str; } @@ -406,7 +424,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { #define _X(_type, _dtype) \ { \ - *((_type*) out) = dout._dtype; \ + convert_types(dout._dtype, *((_type*) out)); \ } __PLUGIN_STATETYPE_SWITCH(typeinfo_to_state_type(i.info())); #undef _X @@ -423,7 +441,7 @@ struct plugin_table_wrapper: public libsinsp::state::table // 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::PT_CHARBUF) + if (i.info().index() == libsinsp::state::typeinfo::index_t::TI_STRING) { v.str = *(const char**) in; } @@ -573,6 +591,11 @@ struct plugin_table_wrapper: public libsinsp::state::table // 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::shared_ptr(new plugin_table_entry(m_owner, m_input, m_dyn_fields, res, false)); } @@ -667,490 +690,543 @@ template<> void plugin_table_wrapper::get_key_as_data(const bool& key, ss_ out.b = key; } -// wraps instances of libsinsp::state::table and makes them comply -// to the plugin API state tables definitions. -struct sinsp_table_wrapper +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); +} + +// +// sinsp_field_accessor_wrapper implementation +// +sinsp_plugin::sinsp_field_accessor_wrapper::~sinsp_field_accessor_wrapper() { - // wraps a dynamic or a static field accessor and its type - struct field_accessor_wrapper + if (!accessor) { - void* accessor; - bool dynamic; - ss_plugin_state_type data_type; - }; + 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 +} - template - explicit sinsp_table_wrapper(sinsp_plugin* p, libsinsp::state::table* t) - : m_owner_plugin(p), m_key_type(typeinfo_to_state_type(t->key_info())), - m_table(t), m_field_list(), m_table_plugin_owner(nullptr), m_table_plugin_input(nullptr) +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; +} + +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) { - // 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(); - } + throw sinsp_exception("null table assigned to sinsp table wrapper"); + } + if (!p) + { + throw sinsp_exception("null plugin assigned to sinsp table wrapper"); } - sinsp_table_wrapper() = delete; - sinsp_table_wrapper(sinsp_table_wrapper&&) = default; - sinsp_table_wrapper& operator = (sinsp_table_wrapper&&) = default; - sinsp_table_wrapper(const sinsp_table_wrapper& s) = delete; - sinsp_table_wrapper& operator = (const sinsp_table_wrapper& s) = delete; - virtual ~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) { - for (auto& acc : m_field_accessors) - { - #define _X(_type, _dtype) \ - { \ - if (acc.second.dynamic) \ - { \ - delete static_cast*>(acc.second.accessor); \ - } \ - else \ - { \ - delete static_cast*>(acc.second.accessor); \ - } \ - break; \ - } - std::string tmp; - __CATCH_ERR_MSG(tmp, { - __PLUGIN_STATETYPE_SWITCH(acc.second.data_type); - }); - #undef _X - } + m_table_plugin_owner = pt->m_owner; + m_table_plugin_input = pt->m_input.get(); } +} - sinsp_plugin* m_owner_plugin; - ss_plugin_state_type m_key_type; - libsinsp::state::base_table* m_table; - std::vector m_field_list; - std::unordered_map m_field_accessors; - const sinsp_plugin* m_table_plugin_owner; - ss_plugin_table_input* m_table_plugin_input; +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; +} - static const ss_plugin_table_fieldinfo* list_fields(ss_plugin_table_t* _t, uint32_t* nfields) +bool sinsp_plugin::sinsp_table_wrapper::is_set() const +{ + return m_table_plugin_input != nullptr || m_table != nullptr; +} + +const ss_plugin_table_fieldinfo* sinsp_plugin::sinsp_table_wrapper::list_fields(ss_plugin_table_t* _t, uint32_t* nfields) +{ + auto t = static_cast(_t); + + if (t->m_table_plugin_input) { - 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) { - 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; + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_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; } - static ss_plugin_table_field_t* get_field(ss_plugin_table_t* _t, const char* name, ss_plugin_state_type data_type) - { - auto t = static_cast(_t); - - if (t->m_table_plugin_input) + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + t->m_field_list.clear(); + for (auto& info : *t->m_table->static_fields()) { - 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; + 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; +} - 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); - } +ss_plugin_table_field_t* sinsp_plugin::sinsp_table_wrapper::get_field(ss_plugin_table_t* _t, const char* name, ss_plugin_state_type data_type) +{ + auto t = static_cast(_t); - 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)); - } - }); + 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; + } - #define _X(_type, _dtype) \ - { \ - auto acc = fixed_it->second.new_accessor<_type>(); \ - sinsp_table_wrapper::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_field_accessors[name] = acc_wrap; \ - return &t->m_field_accessors[name]; \ + 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); } - __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_table_wrapper::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_field_accessors[name] = acc_wrap; \ - return &t->m_field_accessors[name]; \ + 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)); } - __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; + #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]; \ } - - static ss_plugin_table_field_t* add_field(ss_plugin_table_t* _t, const char* name, ss_plugin_state_type data_type) - { - auto t = static_cast(_t); - - if (t->m_table_plugin_input) + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + if (fixed_it != t->m_table->static_fields()->end()) { - 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) + if (data_type != typeinfo_to_state_type(fixed_it->second.info())) { - t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); - } - return ret; + throw sinsp_exception("incompatible data types for static field: " + std::string(name)); + } + __PLUGIN_STATETYPE_SWITCH(data_type); } + }); + #undef _X - if (t->m_table->static_fields()->find(name) != t->m_table->static_fields()->end()) + #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()) { - 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, { + 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); - return get_field(_t, name, data_type); - }); - #undef _X + } + throw sinsp_exception("undefined field '" + std::string(name) + "' in table '" + t->m_table->name() + "'"); + }); + #undef _X + + return NULL; +} + +ss_plugin_table_field_t* sinsp_plugin::sinsp_table_wrapper::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; } - static const char* get_name(ss_plugin_table_t* _t) + if (t->m_table_plugin_input) { - 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->add_table_field(pt, name, data_type); + if (ret == NULL) { - return t->m_table_plugin_input->name; + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); } + return ret; + } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - return t->m_table->name().c_str(); - }); + 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; +} + +const char* sinsp_plugin::sinsp_table_wrapper::get_name(ss_plugin_table_t* _t) +{ + auto t = static_cast(_t); - static uint64_t get_size(ss_plugin_table_t* _t) + if (t->m_table_plugin_input) { - auto t = static_cast(_t); + return t->m_table_plugin_input->name; + } - 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; - } + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + return t->m_table->name().c_str(); + }); + return NULL; +} - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - return t->m_table->entries_count(); - }); - return ((uint64_t) -1); - } +uint64_t sinsp_plugin::sinsp_table_wrapper::get_size(ss_plugin_table_t* _t) +{ + auto t = static_cast(_t); - static ss_plugin_table_entry_t* get_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key) + if (t->m_table_plugin_input) { - 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)) { - 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; - } - - // 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); \ - auto ret = tt->get_entry(key->_dtype); \ - if (ret != nullptr) \ - { \ - return static_cast(ret.get()); \ - } \ - return NULL; \ + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_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; } - static 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); + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + return t->m_table->entries_count(); + }); + return ((uint64_t) -1); +} - static void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) - { - auto t = static_cast(_t); +ss_plugin_table_entry_t* sinsp_plugin::sinsp_table_wrapper::get_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key) +{ + auto t = static_cast(_t); - if (t->m_table_plugin_input) + 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) { - auto pt = t->m_table_plugin_input->table; - t->m_table_plugin_input->reader_ext->release_table_entry(pt, _e); - return; + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); } + return ret; + } - // there's nothing to do here, because plugins borrow raw pointers - // from libsinsp's tables' entries shared pointers - // todo(jasondellaluce): should we actually make plugins own some memory, - // to guarantee that the shared_ptr returned is properly refcounted? + // 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; +} - static ss_plugin_bool iterate_entries(ss_plugin_table_t* _t, ss_plugin_table_iterator_func_t it, ss_plugin_table_iterator_state_t* s) + +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 t = static_cast(_t); + auto pt = t->m_table_plugin_input->table; + t->m_table_plugin_input->reader_ext->release_table_entry(pt, _e); + return; + } - 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); - } + static_cast*>(_e)->reset(); +} - std::function iter = [it, s](auto& e) - { - return it(s, static_cast(&e)) != 0; - }; +ss_plugin_bool sinsp_plugin::sinsp_table_wrapper::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); - #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 + 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; + }; - return false; + #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; +} - static ss_plugin_rc clear(ss_plugin_table_t* _t) - { - auto t = static_cast(_t); +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) + 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) { - 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; + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_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; } - static ss_plugin_rc erase_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key) - { - auto t = static_cast(_t); + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + t->m_table->clear_entries(); + return SS_PLUGIN_SUCCESS; + }); + return SS_PLUGIN_FAILURE; +} - if (t->m_table_plugin_input) +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) { - 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; + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_owner->get_last_error(); } + return ret; + } - #define _X(_type, _dtype) \ + #define _X(_type, _dtype) \ + { \ + _type kk; \ + convert_types(key->_dtype, kk); \ + if (static_cast*>(t->m_table)->erase_entry(kk)) \ { \ - if (static_cast*>(t->m_table)->erase_entry(key->_dtype)) \ - { \ - return SS_PLUGIN_SUCCESS; \ - } \ - else \ - { \ - t->m_owner_plugin->m_last_owner_err = "table entry not found"; \ - return SS_PLUGIN_FAILURE; \ - } \ - } - __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 SS_PLUGIN_SUCCESS; \ + } \ + else \ + { \ + t->m_owner_plugin->m_last_owner_err = "table entry not found"; \ + return SS_PLUGIN_FAILURE; \ + } \ } + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + __PLUGIN_STATETYPE_SWITCH(t->m_key_type); + }); + #undef _X + return SS_PLUGIN_FAILURE; +} - static ss_plugin_table_entry_t* create_table_entry(ss_plugin_table_t* _t) - { - auto t = static_cast(_t); +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) + 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) { - 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; + 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(); \ - return static_cast(ret); \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - __PLUGIN_STATETYPE_SWITCH(t->m_key_type); - }); - #undef _X - return NULL; + #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); \ } + __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { + __PLUGIN_STATETYPE_SWITCH(t->m_key_type); + }); + #undef _X + return NULL; +} - static void destroy_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) - { - auto t = static_cast(_t); +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); - } + 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); \ - break; \ - } - __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { - __PLUGIN_STATETYPE_SWITCH(t->m_key_type); - }); - #undef _X + #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 +} - static ss_plugin_table_entry_t* add_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key, ss_plugin_table_entry_t* _e) - { - auto t = static_cast(_t); +ss_plugin_table_entry_t* sinsp_plugin::sinsp_table_wrapper::add_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key, ss_plugin_table_entry_t* _e) +{ + auto t = static_cast(_t); - if (t->m_table_plugin_input) + 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) { - 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); \ - auto tt = static_cast*>(t->m_table); \ - auto ret = tt->add_entry(key->_dtype, std::move(ptr)).get(); \ - return static_cast(ret); \ + t->m_owner_plugin->m_last_owner_err = t->m_table_plugin_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; } - static 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); -}; + #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_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) +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); @@ -1165,33 +1241,54 @@ ss_plugin_rc sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, ss_plu return ret; } - auto a = static_cast(f); - auto e = static_cast(_e); + 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_dynamic_field<_type>(*aa, out->_dtype); \ + e->get()->get_dynamic_field<_type>(*aa, out->_dtype); \ } \ else \ { \ auto aa = static_cast*>(a->accessor); \ - e->get_static_field<_type>(*aa, out->_dtype); \ + e->get()->get_static_field<_type>(*aa, out->_dtype); \ } \ - return SS_PLUGIN_SUCCESS; \ + res = SS_PLUGIN_SUCCESS; \ + break; \ } __CATCH_ERR_MSG(t->m_owner_plugin->m_last_owner_err, { __PLUGIN_STATETYPE_SWITCH(a->data_type); }); #undef _X - return SS_PLUGIN_FAILURE; + + #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; } -ss_plugin_rc 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) +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; @@ -1203,19 +1300,31 @@ ss_plugin_rc sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, ss_pl return ret; } - auto a = static_cast(f); - auto e = static_cast(_e); + 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); \ - e->set_dynamic_field<_type>(*aa, in->_dtype); \ + _type val; \ + convert_types(in->_dtype, val); \ + e->get()->set_dynamic_field<_type>(*aa, val); \ } \ else \ { \ auto aa = static_cast*>(a->accessor); \ - e->set_static_field<_type>(*aa, in->_dtype); \ + _type val; \ + convert_types(in->_dtype, val); \ + e->get()->set_static_field<_type>(*aa, val); \ } \ return SS_PLUGIN_SUCCESS; \ } @@ -1226,6 +1335,77 @@ ss_plugin_rc sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, ss_pl return SS_PLUGIN_FAILURE; } +// +// 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; + } +} + + // the following table api symbols act as dispatcher for the table API // interface, which is implemented through the type ss_plugin_table_input. // For sinsp-defined tables, the ss_plugin_table_input is a wrapper around @@ -1321,6 +1501,9 @@ static ss_plugin_rc dispatch_write_entry_field(ss_plugin_table_t* _t, ss_plugin_ return t->writer_ext->write_entry_field(t->table, e, f, in); } +// +// sinsp_plugin table helpers implementation +// void sinsp_plugin::table_field_api(ss_plugin_table_fields_vtable& out, ss_plugin_table_fields_vtable_ext& extout) { extout.list_table_fields = dispatch_list_fields; @@ -1382,15 +1565,6 @@ ss_plugin_table_info* sinsp_plugin::table_api_list_tables(ss_plugin_owner_t* o, return NULL; } -void sinsp_plugin::accessed_table_input_deleter::operator()(ss_plugin_table_input* in) -{ - delete static_cast(in->table); - delete in->reader_ext; - delete in->writer_ext; - delete in->fields_ext; - delete in; -} - ss_plugin_table_t* sinsp_plugin::table_api_get_table(ss_plugin_owner_t *o, const char *name, ss_plugin_state_type key_type) { auto p = static_cast(o); @@ -1408,53 +1582,18 @@ ss_plugin_table_t* sinsp_plugin::table_api_get_table(ss_plugin_owner_t *o, const { \ return NULL; \ } \ - accessed_table_t res(new ss_plugin_table_input(), accessed_table_input_deleter()); \ - auto state = new sinsp_table_wrapper(p, t); \ - res->table = static_cast(state); \ - res->name = state->m_table->name().c_str(); \ - res->key_type = state->m_key_type; \ - res->reader_ext = new ss_plugin_table_reader_vtable_ext(); \ - res->writer_ext = new ss_plugin_table_writer_vtable_ext(); \ - res->fields_ext = new ss_plugin_table_fields_vtable_ext(); \ - res->reader_ext->get_table_name = sinsp_table_wrapper::get_name; \ - res->reader_ext->get_table_size = sinsp_table_wrapper::get_size; \ - res->reader_ext->get_table_entry = sinsp_table_wrapper::get_entry; \ - res->reader_ext->read_entry_field = sinsp_table_wrapper::read_entry_field; \ - res->reader_ext->release_table_entry = sinsp_table_wrapper::release_table_entry; \ - res->reader_ext->iterate_entries = sinsp_table_wrapper::iterate_entries; \ - res->writer_ext->clear_table = sinsp_table_wrapper::clear; \ - res->writer_ext->erase_table_entry = sinsp_table_wrapper::erase_entry; \ - res->writer_ext->create_table_entry = sinsp_table_wrapper::create_table_entry; \ - res->writer_ext->destroy_table_entry = sinsp_table_wrapper::destroy_table_entry; \ - res->writer_ext->add_table_entry = sinsp_table_wrapper::add_entry; \ - res->writer_ext->write_entry_field = sinsp_table_wrapper::write_entry_field; \ - res->fields_ext->list_table_fields = sinsp_table_wrapper::list_fields; \ - res->fields_ext->add_table_field = sinsp_table_wrapper::add_field; \ - res->fields_ext->get_table_field = sinsp_table_wrapper::get_field; \ - res->reader.get_table_name = res->reader_ext->get_table_name; \ - res->reader.get_table_size = res->reader_ext->get_table_size; \ - res->reader.get_table_entry = res->reader_ext->get_table_entry; \ - res->reader.read_entry_field = res->reader_ext->read_entry_field; \ - res->writer.clear_table = res->writer_ext->clear_table; \ - res->writer.erase_table_entry = res->writer_ext->erase_table_entry; \ - res->writer.create_table_entry = res->writer_ext->create_table_entry; \ - res->writer.destroy_table_entry = res->writer_ext->destroy_table_entry; \ - res->writer.add_table_entry = res->writer_ext->add_table_entry; \ - res->writer.write_entry_field = res->writer_ext->write_entry_field; \ - res->fields.list_table_fields = res->fields_ext->list_table_fields; \ - res->fields.add_table_field = res->fields_ext->add_table_field; \ - res->fields.get_table_field = res->fields_ext->get_table_field; \ - p->m_accessed_tables[name] = std::move(res); \ - return p->m_accessed_tables[name].get(); \ + p->m_accessed_tables[name].wrapper.set(p, t); \ + p->m_accessed_tables[name].update(); \ + return static_cast(&p->m_accessed_tables[name].input); \ }; __CATCH_ERR_MSG(p->m_last_owner_err, { - const auto& tables = p->m_accessed_tables; + auto& tables = p->m_accessed_tables; auto it = tables.find(name); if (it == tables.end()) { __PLUGIN_STATETYPE_SWITCH(key_type); } - return it->second.get(); + return static_cast(&it->second.input); }); #undef _X return NULL; @@ -1467,7 +1606,7 @@ ss_plugin_rc sinsp_plugin::table_api_add_table(ss_plugin_owner_t *o, const ss_pl { \ auto t = new plugin_table_wrapper<_type>(p, in); \ p->m_table_registry->add_table(t); \ - p->m_owned_tables[in->name] = sinsp_plugin::owned_table_t(t); \ + p->m_owned_tables[in->name] = std::unique_ptr(t); \ break; \ } __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 b4562c8956..7ee5cf1dec 100644 --- a/userspace/libsinsp/state/dynamic_struct.h +++ b/userspace/libsinsp/state/dynamic_struct.h @@ -100,7 +100,9 @@ class dynamic_struct */ inline bool valid() const { - return m_index != (size_t) -1; + // 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_index != typeinfo::index_t::TI_TABLE; } /** @@ -236,6 +238,11 @@ class dynamic_struct protected: virtual const field_info& add_field_info(const field_info& field) { + if (field.info().index() == typeinfo::index_t::TI_TABLE) + { + throw sinsp_exception("dynamic fields of type table are not supported"); + } + const auto &it = m_definitions.find(field.name()); if (it != m_definitions.end()) { @@ -337,7 +344,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() == PT_CHARBUF) + if (i.info().index() == typeinfo::index_t::TI_STRING) { *((const char**) out) = ((const std::string*) buf)->c_str(); } @@ -356,7 +363,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() == PT_CHARBUF) + if (i.info().index() == typeinfo::index_t::TI_STRING) { *((std::string*) buf) = *((const char**) in); } diff --git a/userspace/libsinsp/state/type_info.h b/userspace/libsinsp/state/type_info.h index 7a8efaa99b..8bc999c054 100644 --- a/userspace/libsinsp/state/type_info.h +++ b/userspace/libsinsp/state/type_info.h @@ -39,10 +39,22 @@ class typeinfo public: /** * @brief Numeric identifier of a supported type. - * This reuses the same type enumerative provided by libscap for event - * params to avoid duplicating definitions. */ - using index_t = ppm_param_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. @@ -139,18 +151,21 @@ class typeinfo void (*m_destroy)(void*); }; -// below is the manually-controlled list of all the supported types +class base_table; -template<> inline typeinfo typeinfo::of() { return _build("bool", PT_BOOL); } -template<> inline typeinfo typeinfo::of() { return _build("int8", PT_INT8); } -template<> inline typeinfo typeinfo::of() { return _build("int16", PT_INT16); } -template<> inline typeinfo typeinfo::of() { return _build("int32", PT_INT32); } -template<> inline typeinfo typeinfo::of() { return _build("int64", PT_INT64); } -template<> inline typeinfo typeinfo::of() { return _build("uint8", PT_UINT8); } -template<> inline typeinfo typeinfo::of() { return _build("uint16", PT_UINT16); } -template<> inline typeinfo typeinfo::of() { return _build("uint32", PT_UINT32); } -template<> inline typeinfo typeinfo::of() { return _build("uint64", PT_UINT64); } -template<> inline typeinfo typeinfo::of() { return _build("string", PT_CHARBUF); } +// below is the manually-controlled list of all the supported types +template<> inline typeinfo typeinfo::of() { return _build("bool", TI_BOOL); } +template<> inline typeinfo typeinfo::of() { return _build("int8", TI_INT8); } +template<> inline typeinfo typeinfo::of() { return _build("int16", TI_INT16); } +template<> inline typeinfo typeinfo::of() { return _build("int32", TI_INT32); } +template<> inline typeinfo typeinfo::of() { return _build("int64", TI_INT64); } +template<> inline typeinfo typeinfo::of() { return _build("uint8", TI_UINT8); } +template<> inline typeinfo typeinfo::of() { return _build("uint16", TI_UINT16); } +template<> inline typeinfo typeinfo::of() { return _build("uint32", TI_UINT32); } +template<> inline typeinfo typeinfo::of() { return _build("uint64", TI_UINT64); } +template<> inline typeinfo typeinfo::of() { return _build("string", TI_STRING); } +template<> inline typeinfo typeinfo::of() { return _build("table", TI_TABLE); } +template<> inline typeinfo typeinfo::of() { return _build("table", TI_TABLE); } }; // state }; // libsinsp diff --git a/userspace/libsinsp/test/plugins/tables.cpp b/userspace/libsinsp/test/plugins/tables.cpp index 5a031855dc..f63b6a54dd 100644 --- a/userspace/libsinsp/test/plugins/tables.cpp +++ b/userspace/libsinsp/test/plugins/tables.cpp @@ -282,6 +282,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp // get non-existing thread step++; { + in->table_reader_ext->release_table_entry(ps->thread_table, thread); tmp.s64 = s_new_thread_tid; thread = in->table_reader_ext->get_table_entry(ps->thread_table, &tmp); if (thread) @@ -325,8 +326,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp fprintf(stderr, "table_reader.get_table_size (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); exit(1); } - tmp.s64 = s_new_thread_tid; - in->table_reader_ext->release_table_entry(ps->thread_table, &tmp); + in->table_reader_ext->release_table_entry(ps->thread_table, thread); } // get newly-created thread @@ -402,8 +402,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp exit(1); } - tmp.s64 = s_new_thread_tid; - in->table_reader_ext->release_table_entry(ps->thread_table, &tmp); + in->table_reader_ext->release_table_entry(ps->thread_table, thread); } // erasing an unknown thread @@ -521,8 +520,8 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp fprintf(stderr, "table_reader.write_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); exit(1); } - tmp.s64 = 1; - in->table_reader_ext->release_table_entry(ps->thread_table, &tmp); + + in->table_reader_ext->release_table_entry(ps->thread_table, thread); } return SS_PLUGIN_SUCCESS;