From 7a178c11ffb167de4f14fcdc1c54b462af8aa73a Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:25:50 +0200 Subject: [PATCH 01/66] fix(build): build libbpf with -fPIC Otherwise we can't link libbpf.a into a shared library (libscap.so) Signed-off-by: Grzegorz Nosek --- cmake/modules/libbpf.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/libbpf.cmake b/cmake/modules/libbpf.cmake index ce7ceb0e01..9511738134 100644 --- a/cmake/modules/libbpf.cmake +++ b/cmake/modules/libbpf.cmake @@ -26,7 +26,7 @@ else() URL_HASH "SHA256=32b0c41eabfbbe8e0c8aea784d7495387ff9171b5a338480a8fbaceb9da8d5e5" CONFIGURE_COMMAND mkdir -p build root - BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} BUILD_STATIC_ONLY=y OBJDIR=${LIBBPF_BUILD_DIR}/build DESTDIR=${LIBBPF_BUILD_DIR}/root NO_PKG_CONFIG=1 "EXTRA_CFLAGS=-I${LIBELF_INCLUDE} -I${ZLIB_INCLUDE}" "LDFLAGS=-Wl,-Bstatic" "EXTRA_LDFLAGS=-L${LIBELF_SRC}/libelf/libelf -L${ZLIB_SRC}" -C ${LIBBPF_SRC}/libbpf/src install install_uapi_headers + BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} BUILD_STATIC_ONLY=y OBJDIR=${LIBBPF_BUILD_DIR}/build DESTDIR=${LIBBPF_BUILD_DIR}/root NO_PKG_CONFIG=1 "EXTRA_CFLAGS=-fPIC -I${LIBELF_INCLUDE} -I${ZLIB_INCLUDE}" "LDFLAGS=-Wl,-Bstatic" "EXTRA_LDFLAGS=-L${LIBELF_SRC}/libelf/libelf -L${ZLIB_SRC}" -C ${LIBBPF_SRC}/libbpf/src install install_uapi_headers INSTALL_COMMAND "" UPDATE_COMMAND "" ) From c7448b176731c431cdcbbbfd92ae986b77e0299e Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 14:11:19 +0200 Subject: [PATCH 02/66] fix(build): link shared libraries with libelf.so A system libelf.a won't probably be compiled with -fPIC, meaning we can't link it into a .so. Use libelf.so in that case. (the above "probably" is true at least on Ubuntu 22.04) Signed-off-by: Grzegorz Nosek --- cmake/modules/libelf.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/modules/libelf.cmake b/cmake/modules/libelf.cmake index 33a4cf8d4f..b567366929 100644 --- a/cmake/modules/libelf.cmake +++ b/cmake/modules/libelf.cmake @@ -7,7 +7,12 @@ if(LIBELF_INCLUDE) # we already have LIBELF elseif(NOT USE_BUNDLED_LIBELF) find_path(LIBELF_INCLUDE elf.h PATH_SUFFIXES elf) - find_library(LIBELF_LIB NAMES libelf.a libelf.so) + if(BUILD_SHARED_LIBS) + set(LIBELF_LIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + set(LIBELF_LIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + find_library(LIBELF_LIB NAMES libelf${LIBELF_LIB_SUFFIX}) if(LIBELF_LIB) message(STATUS "Found LIBELF: include: ${LIBELF_INCLUDE}, lib: ${LIBELF_LIB}") else() From 355ad0e364d90ebdbb71b7c98889892644c86aea Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:29:27 +0200 Subject: [PATCH 03/66] cleanup(build): remove duplicated code We always have a `libbpf` target now, even if not using bundled libbpf Signed-off-by: Grzegorz Nosek --- driver/modern_bpf/CMakeLists.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/driver/modern_bpf/CMakeLists.txt b/driver/modern_bpf/CMakeLists.txt index cfccfac7f2..fa1f88fa66 100644 --- a/driver/modern_bpf/CMakeLists.txt +++ b/driver/modern_bpf/CMakeLists.txt @@ -238,9 +238,6 @@ foreach(BPF_C_FILE ${BPF_C_FILES}) set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${file_stem}.bpf.o) -## TODO: we need to clean this! -## Please note: that the `libbpf` target exists only if we use `USE_BUNDLED_LIBBPF` -if(USE_BUNDLED_LIBBPF) add_custom_command( OUTPUT ${BPF_O_FILE} COMMAND ${MODERN_CLANG_EXE} ${CLANG_FLAGS} ${CLANG_SYSTEM_INCLUDES} -c ${BPF_C_FILE} -o ${BPF_O_FILE} @@ -249,15 +246,6 @@ if(USE_BUNDLED_LIBBPF) DEPENDS ${BPF_C_FILE} ${BPF_H_FILES} COMMENT "${MODERN_BPF_LOG_PREFIX} Building BPF object: ${BPF_O_FILE}" ) -else() - add_custom_command( - OUTPUT ${BPF_O_FILE} - COMMAND ${MODERN_CLANG_EXE} ${CLANG_FLAGS} ${CLANG_SYSTEM_INCLUDES} -c ${BPF_C_FILE} -o ${BPF_O_FILE} - VERBATIM - DEPENDS ${BPF_C_FILE} ${BPF_H_FILES} - COMMENT "${MODERN_BPF_LOG_PREFIX} Building BPF object: ${BPF_O_FILE}" - ) -endif() list(APPEND BPF_OBJECT_FILES ${BPF_O_FILE}) endforeach() From 704a5eee1e234cdd6e87e56145805cc3bd4bc840 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:31:33 +0200 Subject: [PATCH 04/66] cleanup(build): make scap_platform_util a static library It's too small to be worth shipping as a separate .so Signed-off-by: Grzegorz Nosek --- userspace/libscap/CMakeLists.txt | 2 +- userspace/libscap/libscap.pc.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index 5822454a08..bb081a4f79 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -88,9 +88,9 @@ add_library(scap set_scap_target_properties(scap) add_library(scap_platform_util + STATIC scap_iflist.c scap_proc_util.c) -set_scap_target_properties(scap_platform_util) if(NOT MINIMAL_BUILD) target_link_libraries(scap diff --git a/userspace/libscap/libscap.pc.in b/userspace/libscap/libscap.pc.in index d8b09076e8..553cc5a7e5 100644 --- a/userspace/libscap/libscap.pc.in +++ b/userspace/libscap/libscap.pc.in @@ -6,5 +6,5 @@ Name: libscap Description: lib for System CAPture Version: @FALCOSECURITY_LIBS_VERSION@ -Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap -lscap_error -lscap_platform -lscap_platform_util @SCAP_LINK_LIBRARIES_FLAGS@ +Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap -lscap_error -lscap_platform @SCAP_LINK_LIBRARIES_FLAGS@ Cflags: -I${includedir}/@LIBS_PACKAGE_NAME@/userspace/libscap From 3344ffcb1e12938e9613357b55d7df554c6e010f Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:32:41 +0200 Subject: [PATCH 05/66] cleanup(build): make scap_error a static library It's too small to be worth shipping as a separate .so Signed-off-by: Grzegorz Nosek --- userspace/libscap/CMakeLists.txt | 4 +--- userspace/libscap/libscap.pc.in | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index bb081a4f79..e4a43f3443 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -97,12 +97,10 @@ target_link_libraries(scap "${ZLIB_LIB}") endif() -add_library(scap_error strerror.c) +add_library(scap_error STATIC strerror.c) target_link_libraries(scap scap_error) -set_scap_target_properties(scap_error) - if(CMAKE_SYSTEM_NAME MATCHES "Linux") option(BUILD_LIBSCAP_EXAMPLES "Build libscap examples" ON) include(FindMakedev) diff --git a/userspace/libscap/libscap.pc.in b/userspace/libscap/libscap.pc.in index 553cc5a7e5..82dae0279d 100644 --- a/userspace/libscap/libscap.pc.in +++ b/userspace/libscap/libscap.pc.in @@ -6,5 +6,5 @@ Name: libscap Description: lib for System CAPture Version: @FALCOSECURITY_LIBS_VERSION@ -Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap -lscap_error -lscap_platform @SCAP_LINK_LIBRARIES_FLAGS@ +Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap -lscap_platform @SCAP_LINK_LIBRARIES_FLAGS@ Cflags: -I${includedir}/@LIBS_PACKAGE_NAME@/userspace/libscap From a284c0ef2a3a9f455fefd9352d17a1f34637b0f7 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:33:19 +0200 Subject: [PATCH 06/66] cleanup(build): make scap_event_schema a static library It's too small to be worth shipping as a separate .so Signed-off-by: Grzegorz Nosek --- userspace/libscap/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index e4a43f3443..3e97b986fc 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -129,6 +129,7 @@ elseif(APPLE) endif() add_library(scap_event_schema + STATIC scap_event.c ppm_sc_names.c ../../driver/dynamic_params_table.c @@ -140,8 +141,6 @@ add_library(scap_event_schema target_link_libraries(scap scap_event_schema) -set_scap_target_properties(scap_event_schema) - if(CMAKE_SYSTEM_NAME MATCHES "Linux") add_library(driver_event_schema ../../driver/fillers_table.c) From 1bdc87c3e143c4c647619e5f4a4a563688591a4d Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:34:16 +0200 Subject: [PATCH 07/66] cleanup(build): make driver_event_schema a static library It's too small to be worth shipping as a separate .so. Also note that the set_scap_target_properties was wrong Signed-off-by: Grzegorz Nosek --- userspace/libscap/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index 3e97b986fc..bdbfdcafb9 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -143,9 +143,9 @@ target_link_libraries(scap scap_event_schema) if(CMAKE_SYSTEM_NAME MATCHES "Linux") add_library(driver_event_schema + STATIC ../../driver/fillers_table.c) target_link_libraries(scap_event_schema driver_event_schema) - set_scap_target_properties(scap_event_schema) endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") From 34d8d28c1f04c57b0dcc8c8d47c4c7c06e9654ec Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:35:18 +0200 Subject: [PATCH 08/66] cleanup(build): make scap_engine_util a static library It's too small to be worth shipping as a separate .so. Signed-off-by: Grzegorz Nosek --- userspace/libscap/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index bdbfdcafb9..2b73f213ad 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -150,12 +150,12 @@ endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") add_library(scap_engine_util + STATIC scap_engine_util.c ringbuffer/devset.c ringbuffer/ringbuffer.c) target_link_libraries(scap scap_engine_util) - set_scap_target_properties(scap_engine_util) endif() add_definitions(-DHAS_ENGINE_NOOP) From 710e16db4effd138b37da34ae3e46a71a3e29b1e Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:36:46 +0200 Subject: [PATCH 09/66] cleanup(build): make scap_savefile_engine a static library We can't (yet) easily build it as a shared library due to circular dependencies but we should be okay with building it as a static library linked directly to libscap.so. Signed-off-by: Grzegorz Nosek --- userspace/libscap/CMakeLists.txt | 6 +--- .../libscap/engine/savefile/CMakeLists.txt | 29 +++++++++---------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index 2b73f213ad..47699d4987 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -171,11 +171,7 @@ endif() add_definitions(-DHAS_ENGINE_SAVEFILE) add_subdirectory(engine/savefile) -# The static and shared build differs here because a shared scap_engine_savefile -# will result in circular dependencies. -if(NOT BUILD_SHARED_LIBS) - target_link_libraries(scap scap_engine_savefile) -endif() +target_link_libraries(scap scap_engine_savefile) add_definitions(-DHAS_ENGINE_SOURCE_PLUGIN) add_subdirectory(engine/source_plugin) diff --git a/userspace/libscap/engine/savefile/CMakeLists.txt b/userspace/libscap/engine/savefile/CMakeLists.txt index 4abc0fa230..1dc3536392 100644 --- a/userspace/libscap/engine/savefile/CMakeLists.txt +++ b/userspace/libscap/engine/savefile/CMakeLists.txt @@ -1,19 +1,16 @@ include_directories(${LIBSCAP_INCLUDE_DIRS} ../noop) -set(scap_engine_savefile_sources - ${CMAKE_CURRENT_SOURCE_DIR}/scap_savefile.c - ${CMAKE_CURRENT_SOURCE_DIR}/scap_reader_gzfile.c - ${CMAKE_CURRENT_SOURCE_DIR}/scap_reader_buffered.c) -if (BUILD_SHARED_LIBS) - # Trying to build a shared scap_engine_savefile will result in circular - # dependencies, so just add our sources to scap. - target_sources(scap PRIVATE ${scap_engine_savefile_sources}) -else() - add_library(scap_engine_savefile ${scap_engine_savefile_sources}) +# Since we have circular dependencies between libscap and the savefile engine, +# make this library always static (directly linked into libscap) +add_library(scap_engine_savefile + STATIC + scap_savefile.c + scap_reader_gzfile.c + scap_reader_buffered.c) - if(NOT MINIMAL_BUILD) - add_dependencies(scap_engine_savefile zlib) - endif() - target_link_libraries(scap_engine_savefile scap_engine_noop scap_platform_util ${ZLIB_LIB}) - set_scap_target_properties(scap_engine_savefile) -endif() +target_link_libraries(scap_engine_savefile scap_engine_noop scap_platform_util) + +if(NOT MINIMAL_BUILD) + add_dependencies(scap_engine_savefile zlib) + target_link_libraries(scap_engine_savefile ${ZLIB_LIB}) +endif() \ No newline at end of file From 4f5db70ca7464440f06520dbcc31b14dfc0d36b3 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:38:06 +0200 Subject: [PATCH 10/66] cleanup(build): make scap_engine_noop a static library It's too small to be worth shipping as a separate .so and it's not useful on its own (only as a part of another engine). Signed-off-by: Grzegorz Nosek --- userspace/libscap/engine/noop/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/userspace/libscap/engine/noop/CMakeLists.txt b/userspace/libscap/engine/noop/CMakeLists.txt index e69767909f..15be51a248 100644 --- a/userspace/libscap/engine/noop/CMakeLists.txt +++ b/userspace/libscap/engine/noop/CMakeLists.txt @@ -1,3 +1,2 @@ include_directories(${LIBSCAP_INCLUDE_DIRS}) -add_library(scap_engine_noop noop.c) -set_scap_target_properties(scap_engine_noop) +add_library(scap_engine_noop STATIC noop.c) From 619131e05be7437f2a8d7e6683b41f991a7d912d Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 12:38:42 +0200 Subject: [PATCH 11/66] cleanup(build): make scap_platform a static library It's too small to be worth shipping as a separate .so. Signed-off-by: Grzegorz Nosek --- userspace/libscap/libscap.pc.in | 2 +- userspace/libscap/linux/CMakeLists.txt | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/userspace/libscap/libscap.pc.in b/userspace/libscap/libscap.pc.in index 82dae0279d..de3cf476c6 100644 --- a/userspace/libscap/libscap.pc.in +++ b/userspace/libscap/libscap.pc.in @@ -6,5 +6,5 @@ Name: libscap Description: lib for System CAPture Version: @FALCOSECURITY_LIBS_VERSION@ -Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap -lscap_platform @SCAP_LINK_LIBRARIES_FLAGS@ +Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap @SCAP_LINK_LIBRARIES_FLAGS@ Cflags: -I${includedir}/@LIBS_PACKAGE_NAME@/userspace/libscap diff --git a/userspace/libscap/linux/CMakeLists.txt b/userspace/libscap/linux/CMakeLists.txt index e8baab260e..117ead7e6f 100644 --- a/userspace/libscap/linux/CMakeLists.txt +++ b/userspace/libscap/linux/CMakeLists.txt @@ -1,3 +1,2 @@ -add_library(scap_platform scap_linux_platform.c scap_procs.c scap_fds.c scap_userlist.c scap_iflist.c scap_cgroup.c) -target_link_libraries(scap_platform scap_error scap_platform_util) -set_scap_target_properties(scap_platform) +add_library(scap_platform STATIC scap_linux_platform.c scap_procs.c scap_fds.c scap_userlist.c scap_iflist.c scap_cgroup.c) +target_link_libraries(scap_platform scap_error scap_platform_util) \ No newline at end of file From 3aa48a433c188cede856f13eba531b75134c24e2 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Thu, 20 Jul 2023 13:21:18 +0200 Subject: [PATCH 12/66] fix(build): default to -fPIC when building shared libraries Signed-off-by: Grzegorz Nosek --- cmake/modules/CompilerFlags.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/CompilerFlags.cmake b/cmake/modules/CompilerFlags.cmake index dc9480c6e2..fdcb7fa6bf 100644 --- a/cmake/modules/CompilerFlags.cmake +++ b/cmake/modules/CompilerFlags.cmake @@ -1,6 +1,6 @@ option(BUILD_WARNINGS_AS_ERRORS "Enable building with -Wextra -Werror flags") -option(ENABLE_PIC "Build position independent libraries and executables" OFF) +option(ENABLE_PIC "Build position independent libraries and executables" ${BUILD_SHARED_LIBS}) if(ENABLE_PIC) set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() From 816feb0b5796d36aba74d17de1ed76f50078f6fb Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Wed, 19 Jul 2023 15:38:00 +0000 Subject: [PATCH 13/66] update(userspace/libsinsp/state): make dynamic structs methods virtual Signed-off-by: Jason Dellaluce --- userspace/libsinsp/filterchecks.cpp | 6 +- userspace/libsinsp/state/dynamic_struct.h | 97 ++++++++++++++++------- userspace/libsinsp/state/table.h | 4 +- userspace/libsinsp/state/type_info.h | 41 ++++++++++ userspace/libsinsp/test/state.ut.cpp | 29 ++++--- 5 files changed, 135 insertions(+), 42 deletions(-) diff --git a/userspace/libsinsp/filterchecks.cpp b/userspace/libsinsp/filterchecks.cpp index 9f046db3c1..566da52138 100644 --- a/userspace/libsinsp/filterchecks.cpp +++ b/userspace/libsinsp/filterchecks.cpp @@ -2704,7 +2704,8 @@ uint8_t* sinsp_filter_check_thread::extract_thread_cpu(sinsp_evt *evt, OUT uint3 tcpu = user + system; - uint64_t last_t_tot_cpu = tinfo->get_dynamic_field(*m_thread_dyn_field_accessor.get()); + uint64_t last_t_tot_cpu = 0; + tinfo->get_dynamic_field(*m_thread_dyn_field_accessor.get(), last_t_tot_cpu); if(last_t_tot_cpu != 0) { uint64_t deltaval = tcpu - last_t_tot_cpu; @@ -3137,7 +3138,8 @@ uint8_t* sinsp_filter_check_thread::extract(sinsp_evt *evt, OUT uint32_t* len, b if(tinfo != NULL) { - uint64_t ptot = tinfo->get_dynamic_field(*m_thread_dyn_field_accessor.get()); + uint64_t ptot = 0; + tinfo->get_dynamic_field(*m_thread_dyn_field_accessor.get(), ptot); m_u64val += ptot; tinfo->set_dynamic_field(*m_thread_dyn_field_accessor.get(), m_u64val); RETURN_EXTRACT_VAR(m_u64val); diff --git a/userspace/libsinsp/state/dynamic_struct.h b/userspace/libsinsp/state/dynamic_struct.h index f02f18d981..195431545b 100644 --- a/userspace/libsinsp/state/dynamic_struct.h +++ b/userspace/libsinsp/state/dynamic_struct.h @@ -41,7 +41,20 @@ class dynamic_struct class field_info { public: + template + static field_info build(const std::string& name, size_t index, void* defsptr, bool readonly=false) + { + return field_info(name, index, libsinsp::state::typeinfo::of(), defsptr, readonly); + } + + field_info(const std::string& n, size_t in, const typeinfo& i, void* defsptr, bool r) + : m_readonly(r), + m_index(in), + m_name(n), + m_info(i), + m_defsptr(defsptr) {} field_info(): + m_readonly(true), m_index((size_t) -1), m_name(""), m_info(typeinfo::of()), @@ -65,6 +78,14 @@ class dynamic_struct return !(a == b); }; + /** + * @brief Returns true if the field is read only. + */ + bool readonly() const + { + return m_readonly; + } + /** * @brief Returns true if the field info is valid. */ @@ -81,6 +102,14 @@ class dynamic_struct return m_name; } + /** + * @brief Returns the index of the field. + */ + size_t index() const + { + return m_index; + } + /** * @brief Returns the type info of the field. */ @@ -112,18 +141,7 @@ class dynamic_struct } private: - field_info(const std::string& n, size_t in, const typeinfo& i, void* defsptr) - : m_index(in), - m_name(n), - m_info(i), - m_defsptr(defsptr) { } - - template - static field_info _build(const std::string& name, size_t index, void* defsptr) - { - return field_info(name, index, libsinsp::state::typeinfo::of(), defsptr); - } - + bool m_readonly; size_t m_index; std::string m_name; libsinsp::state::typeinfo m_info; @@ -180,11 +198,6 @@ class dynamic_struct field_infos(const field_infos& s) = delete; field_infos& operator = (const field_infos& s) = delete; - inline const std::unordered_map& fields() const - { - return m_definitions; - } - /** * @brief Adds metadata for a new field to the list. An exception is * thrown if two fields are defined with the same name and with @@ -194,26 +207,37 @@ class dynamic_struct * @param name Display name of the field. */ template - const field_info& add_field(const std::string& name) + inline const field_info& add_field(const std::string& name) { - const auto &it = m_definitions.find(name); + auto field = field_info::build(name, m_definitions.size(), this); + return add_field(field); + } + + virtual const std::unordered_map& fields() + { + return m_definitions; + } + +protected: + virtual const field_info& add_field(const field_info& field) + { + const auto &it = m_definitions.find(field.name()); if (it != m_definitions.end()) { - auto t = libsinsp::state::typeinfo::of(); + const auto& t = field.info(); if (it->second.info() != t) { throw sinsp_exception("multiple definitions of dynamic field with different types in struct: " - + name + ", prevtype=" + it->second.info().name() + ", newtype=" + t.name()); + + field.name() + ", prevtype=" + it->second.info().name() + ", newtype=" + t.name()); } return it->second; } - m_definitions.insert({ name, field_info::_build(name, m_definitions.size(), this) }); - const auto& def = m_definitions.at(name); + m_definitions.insert({ field.name(), field }); + const auto& def = m_definitions.at(field.name()); m_definitions_ordered.push_back(&def); return def; } - private: std::unordered_map m_definitions; std::vector m_definitions_ordered; friend class dynamic_struct; @@ -241,28 +265,32 @@ class dynamic_struct * @brief Accesses a field with the given accessor and reads its value. */ template - inline const T& get_dynamic_field(const field_accessor& a) + inline void get_dynamic_field(const field_accessor& a, T& out) { if (!a.info().valid()) { throw sinsp_exception("can't get invalid field in dynamic struct"); } _check_defsptr(a.info().m_defsptr); - return *(reinterpret_cast(_access_dynamic_field(a.info().m_index))); + get_dynamic_field(a.info(), reinterpret_cast(&out)); } /** * @brief Accesses a field with the given accessor and writes its value. */ template - inline void set_dynamic_field(const field_accessor& a, const T& v) + inline void set_dynamic_field(const field_accessor& a, const T& in) { if (!a.info().valid()) { throw sinsp_exception("can't set invalid field in dynamic struct"); } _check_defsptr(a.info().m_defsptr); - *(reinterpret_cast(_access_dynamic_field(a.info().m_index))) = v; + if (a.info().readonly()) + { + throw sinsp_exception("can't set a read-only dynamic struct field: " + a.info().name()); + } + set_dynamic_field(a.info(), reinterpret_cast(&in)); } /** @@ -278,7 +306,7 @@ class dynamic_struct * The definitions can be set to a non-null value only once, either at * construction time by invoking this method. */ - inline void set_dynamic_fields(const std::shared_ptr& defs) + virtual void set_dynamic_fields(const std::shared_ptr& defs) { if (m_dynamic_fields) { @@ -291,6 +319,17 @@ class dynamic_struct m_dynamic_fields = defs; } +protected: + virtual void get_dynamic_field(const field_info& i, void* out) + { + i.info().copy(_access_dynamic_field(i.m_index), out); + } + + virtual void set_dynamic_field(const field_info& i, const void* in) + { + i.info().copy(in, _access_dynamic_field(i.m_index)); + } + private: inline void _check_defsptr(void* ptr) const { diff --git a/userspace/libsinsp/state/table.h b/userspace/libsinsp/state/table.h index d8b3beb828..62204f623a 100644 --- a/userspace/libsinsp/state/table.h +++ b/userspace/libsinsp/state/table.h @@ -78,7 +78,7 @@ class base_table * for the value data type of this table. This fields will be accessible * for all the entries of this table. */ - inline virtual const static_struct::field_infos& static_fields() const + virtual const static_struct::field_infos& static_fields() const { return m_static_fields; } @@ -91,7 +91,7 @@ class base_table * be allocated and accessible for all the present and future entries * present in the table. */ - inline virtual const std::shared_ptr& dynamic_fields() const + virtual std::shared_ptr dynamic_fields() const { return m_dynamic_fields; } diff --git a/userspace/libsinsp/state/type_info.h b/userspace/libsinsp/state/type_info.h index 70c07e39ad..2e3ac0bfdc 100644 --- a/userspace/libsinsp/state/type_info.h +++ b/userspace/libsinsp/state/type_info.h @@ -16,6 +16,7 @@ limitations under the License. */ #pragma once +#include "../sinsp_public.h" #include "../sinsp_exception.h" #include "../../driver/ppm_events_public.h" @@ -110,6 +111,46 @@ class typeinfo if (p && m_destroy) m_destroy(p); } + inline void copy(const void* from, void* to) const noexcept + { + switch(m_index) + { + case PT_INT8: + *((int8_t*) to) = *((const int8_t*) from); + break; + case PT_INT16: + *((int16_t*) to) = *((const int16_t*) from); + break; + case PT_INT32: + *((int32_t*) to) = *((const int32_t*) from); + break; + case PT_INT64: + *((int64_t*) to) = *((const int64_t*) from); + break; + case PT_UINT8: + *((uint8_t*) to) = *((const uint8_t*) from); + break; + case PT_UINT16: + *((uint16_t*) to) = *((const uint16_t*) from); + break; + case PT_UINT32: + *((uint32_t*) to) = *((const uint32_t*) from); + break; + case PT_UINT64: + *((uint64_t*) to) = *((const uint64_t*) from); + break; + case PT_CHARBUF: + *((std::string*) to) = *((const std::string*) from); + break; + case PT_BOOL: + *((bool*) to) = *((const bool*) from); + break; + default: + ASSERT(false); + break; + } + } + private: inline typeinfo(const char* n, index_t k, size_t s, void (*c)(void*), void (*d)(void*)) : m_name(n), m_index(k), m_size(s), m_construct(c), m_destroy(d) { } diff --git a/userspace/libsinsp/test/state.ut.cpp b/userspace/libsinsp/test/state.ut.cpp index 695938d036..4506397644 100644 --- a/userspace/libsinsp/test/state.ut.cpp +++ b/userspace/libsinsp/test/state.ut.cpp @@ -174,19 +174,25 @@ TEST(dynamic_struct, defs_and_access) ASSERT_ANY_THROW(field_num.new_accessor()); ASSERT_ANY_THROW(field_str.new_accessor()); - ASSERT_EQ(s.get_dynamic_field(acc_num), 0); + uint64_t tmp; + s.get_dynamic_field(acc_num, tmp); + ASSERT_EQ(tmp, 0); s.set_dynamic_field(acc_num, (uint64_t) 6); - ASSERT_EQ(s.get_dynamic_field(acc_num), 6); + s.get_dynamic_field(acc_num, tmp); + ASSERT_EQ(tmp, 6); - ASSERT_EQ(s.get_dynamic_field(acc_str), std::string("")); + std::string tmpstr; + s.get_dynamic_field(acc_str, tmpstr); + ASSERT_EQ(tmpstr, std::string("")); s.set_dynamic_field(acc_str, std::string("hello")); - ASSERT_EQ(s.get_dynamic_field(acc_str), std::string("hello")); + s.get_dynamic_field(acc_str, tmpstr); + ASSERT_EQ(tmpstr, std::string("hello")); // illegal access from an accessor created from different definition list auto fields2 = std::make_shared(); auto field_num2 = fields2->add_field("num"); auto acc_num2 = field_num2.new_accessor(); - ASSERT_ANY_THROW(s.get_dynamic_field(acc_num2)); + ASSERT_ANY_THROW(s.get_dynamic_field(acc_num2, tmp)); } TEST(table_registry, defs_and_access) @@ -303,12 +309,15 @@ TEST(thread_manager, table_access) ASSERT_EQ(addedt->get_static_field(comm_acc), "test"); // add a dynamic field to table + std::string tmpstr; auto dynf_acc = table->dynamic_fields()->add_field("some_new_field").new_accessor(); ASSERT_EQ(table->dynamic_fields()->fields().size(), 1); ASSERT_EQ(addedt->dynamic_fields()->fields().size(), 1); - ASSERT_EQ(addedt->get_dynamic_field(dynf_acc), ""); + addedt->get_dynamic_field(dynf_acc, tmpstr); + ASSERT_EQ(tmpstr, ""); addedt->set_dynamic_field(dynf_acc, std::string("hello")); - ASSERT_EQ(addedt->get_dynamic_field(dynf_acc), "hello"); + addedt->get_dynamic_field(dynf_acc, tmpstr); + ASSERT_EQ(tmpstr, "hello"); // add another thread newt = table->new_entry(); @@ -316,9 +325,11 @@ TEST(thread_manager, table_access) ASSERT_NO_THROW(table->add_entry(1000, std::move(newt))); addedt = table->get_entry(1000); ASSERT_EQ(addedt->get_static_field(tid_acc), (int64_t) 1000); - ASSERT_EQ(addedt->get_dynamic_field(dynf_acc), ""); + addedt->get_dynamic_field(dynf_acc, tmpstr); + ASSERT_EQ(tmpstr, ""); addedt->set_dynamic_field(dynf_acc, std::string("world")); - ASSERT_EQ(addedt->get_dynamic_field(dynf_acc), "world"); + addedt->get_dynamic_field(dynf_acc, tmpstr); + ASSERT_EQ(tmpstr, "world"); // loop over entries int count = 0; From f25afd4d238beeec04e51ec0ad9e6b8254de027e Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Wed, 19 Jul 2023 15:42:36 +0000 Subject: [PATCH 14/66] update(userspace/libsinsp): expose table registry n inspector Signed-off-by: Jason Dellaluce --- userspace/libsinsp/sinsp.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 284b89399e..4181ae06e1 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -998,6 +998,11 @@ class SINSP_PUBLIC sinsp : public capture_stats_source return m_event_sources; } + inline const std::shared_ptr& get_table_registry() const + { + return m_table_registry; + } + uint64_t get_lastevent_ts() const { return m_lastevent_ts; } const std::string& get_host_root() const { return m_host_root; } From 944e625dba5d59d481724eb35db51dd01c72b66b Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Wed, 19 Jul 2023 15:43:07 +0000 Subject: [PATCH 15/66] fix(userspace/libsinsp): adding thread to manager with state API Signed-off-by: Jason Dellaluce --- userspace/libsinsp/threadinfo.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/userspace/libsinsp/threadinfo.h b/userspace/libsinsp/threadinfo.h index 7fe79bd85c..35893a45c9 100644 --- a/userspace/libsinsp/threadinfo.h +++ b/userspace/libsinsp/threadinfo.h @@ -718,11 +718,8 @@ class SINSP_PUBLIC sinsp_thread_manager: public libsinsp::state::table { throw sinsp_exception("unknown entry type added to thread table"); } - if (tinfo->m_tid != key) - { - throw sinsp_exception("key does not match pid of entry added to thread table"); - } entry.release(); + tinfo->m_tid = key; add_thread(tinfo, false); return get_entry(key); } From c5aca4e3b93e8911abf850b83a639860d7bc28a4 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Wed, 19 Jul 2023 15:45:05 +0000 Subject: [PATCH 16/66] update(userspace/libsinsp): support accessing plugins tables from outside plugin API Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 395 +++++++++++++++++++++--- 1 file changed, 357 insertions(+), 38 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index 912f4a55bc..ae59b04afd 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -82,23 +82,253 @@ static inline ss_plugin_state_type typeinfo_to_state_type(const libsinsp::state: } } +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(const char* const& from, std::string& to) +{ + if (!from || *from == '\0') + { + to = ""; + } + else + { + to = from; + } +} + // wraps instances of ss_plugin_table_input and makes them comply // to the libsinsp::state::table state tables definition. -// note(jasondellaluce): for now, we don't support accessing plugin-owned -// tables from sinsp internal components, and as such this wrapper simply -// throws exceptions in case unsafe access is detected. This choice is for both -// reducing the system's complexity, and also because the C -> C++ type -// conversions can become tricky and unsafe, specially given that plugins -// can be implemented in other languages. 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 std::shared_ptr& 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; + std::shared_ptr m_input; + std::vector m_accessors; + + virtual const std::unordered_map& fields() override + { + uint32_t nfields = 0; + auto res = m_input->fields.list_table_fields(m_input->table, &nfields); + if (res == NULL) + { + throw sinsp_exception("plugin table list fields error: " + m_owner->get_last_error()); + } + 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, this, res[i].read_only); \ + } + __PLUGIN_STATETYPE_SWITCH(res[i].field_type); + #undef _X + ds::field_infos::add_field(f); + } + } + + // make sure we have accessors for all of these fields + 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.get_table_field(m_input->table, f.name().c_str(), typeinfo_to_state_type(f.info())); + if (facc == NULL) + { + throw sinsp_exception("plugin table get field error: " + m_owner->get_last_error()); + } + m_accessors[f.index()] = facc; + } + } + return ret; + } + + virtual const ds::field_info& add_field(const ds::field_info& field) override + { + auto ret = m_input->fields.add_table_field(m_input->table, field.name().c_str(), typeinfo_to_state_type(field.info())); + if (ret == NULL) + { + throw sinsp_exception("plugin table list fields error: " + m_owner->get_last_error()); + } + // trigger all updates and obtain the right ref + this->fields(); + return ds::field_infos::add_field(field); + } + }; + + struct plugin_table_entry: public libsinsp::state::table_entry + { + plugin_table_entry( + const sinsp_plugin* o, + const std::shared_ptr& i, + const std::shared_ptr& fields, + ss_plugin_table_entry_t* e, + bool destroy) + : table_entry(fields), + m_owner(o), + m_input(i), + m_entry(e), + m_destroy_entry(destroy) {}; + plugin_table_entry(const plugin_table_entry& o) + { + // note: this is not supposed to ever happen: in the general case, + // m_destroy_entry will be false because plugin_table_entry will not + // own the entry (the table will). The only time in which it is owner + // is when being held in a unique pointer during the + // new_entry -> add_entry flow. + ASSERT(!o.m_destroy_entry); + if (o.m_destroy_entry) + { + throw sinsp_exception("plugin_table_entry can't be copied while being owner of an entry pointer"); + } + m_owner = o.m_owner; + m_input = o.m_input; + m_entry = o.m_entry; + m_destroy_entry = o.m_destroy_entry; + }; + plugin_table_entry& operator = (const plugin_table_entry& o) + { + ASSERT(!o.m_destroy_entry); + if (o.m_destroy_entry) + { + throw sinsp_exception("plugin_table_entry can't be copied while being owner of an entry pointer"); + } + m_owner = o.m_owner; + m_input = o.m_input; + m_entry = o.m_entry; + m_destroy_entry = o.m_destroy_entry; + } + plugin_table_entry(plugin_table_entry&& o) + { + m_owner = o.m_owner; + m_input = o.m_input; + m_entry = o.m_entry; + m_destroy_entry = o.m_destroy_entry; + o.m_destroy_entry = false; + }; + plugin_table_entry& operator = (plugin_table_entry&& o) + { + m_owner = o.m_owner; + m_input = o.m_input; + m_entry = o.m_entry; + m_destroy_entry = o.m_destroy_entry; + o.m_destroy_entry = false; + return *this; + }; + virtual ~plugin_table_entry() + { + if (m_destroy_entry) + { + m_input->writer.destroy_table_entry(m_input->table, m_entry); + } + }; + + const sinsp_plugin* m_owner; + std::shared_ptr m_input; + ss_plugin_table_entry_t* m_entry; + bool m_destroy_entry; + + // note(jasondellaluce): dynamic cast is expensive but this is not expected + // to ever be invoked because we plan of settings 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("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 + { + const auto& infos = get_plugin_field_infos(); + ss_plugin_state_data dout; + auto rc = m_input->reader.read_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &dout); + if (rc != SS_PLUGIN_SUCCESS) + { + throw sinsp_exception("plugin table entry read field error: " + m_owner->get_last_error()); + } + #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; + + #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.write_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &v); + if (rc != SS_PLUGIN_SUCCESS) + { + throw sinsp_exception("plugin table entry write field error: " + m_owner->get_last_error()); + } + } + private: + const plugin_field_infos& get_plugin_field_infos() const + { + if (dynamic_fields() == nullptr) + { + throw sinsp_exception("plugin table entry fields definitions are not set"); + } + // note: casting is safe because we force the plugin_field_infos + // subtype both the constructor and the setter + return *static_cast(dynamic_fields().get()); + } + }; + plugin_table_wrapper(const sinsp_plugin* o, const ss_plugin_table_input* i) - : libsinsp::state::table(i->name, libsinsp::state::static_struct::field_infos()), - m_owner(o), m_input(*i) + : libsinsp::state::table(i->name, ss::field_infos()), + m_owner(o), + m_input(std::make_shared(*i)), + m_static_fields(), + m_dyn_fields(std::make_shared(o, m_input)) { auto t = libsinsp::state::typeinfo::of(); - if (m_input.key_type != typeinfo_to_state_type(t)) + if (m_input->key_type != typeinfo_to_state_type(t)) { throw sinsp_exception("invalid key type for plugin-owned table: " + std::string(t.name())); } @@ -111,59 +341,151 @@ struct plugin_table_wrapper: public libsinsp::state::table plugin_table_wrapper& operator = (const plugin_table_wrapper& s) = delete; const sinsp_plugin* m_owner; - ss_plugin_table_input m_input; + std::shared_ptr m_input; + libsinsp::state::static_struct::field_infos m_static_fields; + std::shared_ptr m_dyn_fields; - inline std::string invalid_access_msg() const + inline std::string invalid_access_msg(const std::string& op) const { - return "can't access plugin-owned table '" + this->name() + "' from inspector"; + return "operation '" + op + "' not supported by plugin-owned table '" + this->name() + "'"; } const libsinsp::state::static_struct::field_infos& static_fields() const override { - throw sinsp_exception(invalid_access_msg()); + // note: always empty, plugin-defined table have no "static" fields, + // all of them are dynamically-discovered at runtime + return m_static_fields; } - const std::shared_ptr& dynamic_fields() const override + std::shared_ptr dynamic_fields() const override { - throw sinsp_exception(invalid_access_msg()); + return m_dyn_fields; } size_t entries_count() const override { - throw sinsp_exception(invalid_access_msg()); + auto res = m_input->reader.get_table_size(m_input->table); + if (res == (uint64_t) -1) + { + throw sinsp_exception(m_owner->get_last_error()); + } + return (size_t) res; } void clear_entries() override { - throw sinsp_exception(invalid_access_msg()); + auto res = m_input->writer.clear_table(m_input->table); + if (res != SS_PLUGIN_SUCCESS) + { + throw sinsp_exception(m_owner->get_last_error()); + } } bool foreach_entry(std::function pred) override { - throw sinsp_exception(invalid_access_msg()); + throw sinsp_exception(invalid_access_msg("foreach")); } std::unique_ptr new_entry() const override { - throw sinsp_exception(invalid_access_msg()); + auto res = m_input->writer.create_table_entry(m_input->table); + if (res == NULL) + { + throw sinsp_exception(m_owner->get_last_error()); + } + return std::unique_ptr(new plugin_table_entry(m_owner, m_input, m_dyn_fields, res, true)); } std::shared_ptr get_entry(const KeyType& key) override { - throw sinsp_exception(invalid_access_msg()); + ss_plugin_state_data keydata; + get_key_data(key, keydata); + auto res = m_input->reader.get_table_entry(m_input->table, &keydata); + if (res == NULL) + { + return nullptr; + } + return std::shared_ptr(new plugin_table_entry(m_owner, m_input, m_dyn_fields, res, false)); } std::shared_ptr add_entry(const KeyType& key, std::unique_ptr e) override { - throw sinsp_exception(invalid_access_msg()); + ASSERT(dynamic_cast(e.get()) != nullptr); + plugin_table_entry* entry = static_cast(e.get()); + ss_plugin_state_data keydata; + get_key_data(key, keydata); + auto res = m_input->writer.add_table_entry(m_input->table, &keydata, entry->m_entry); + if (res == NULL) + { + throw sinsp_exception(m_owner->get_last_error()); + } + entry->m_entry = res; + entry->m_destroy_entry = false; + return std::move(e); } bool erase_entry(const KeyType& key) override { - throw sinsp_exception(invalid_access_msg()); + ss_plugin_state_data keydata; + get_key_data(key, keydata); + auto res = m_input->writer.erase_table_entry(m_input->table, &keydata); + return res == SS_PLUGIN_SUCCESS; } + + private: + void get_key_data(const KeyType& key, ss_plugin_state_data& out); }; +template<> void plugin_table_wrapper::get_key_data(const int8_t& key, ss_plugin_state_data& out) +{ + out.s8 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const int16_t& key, ss_plugin_state_data& out) +{ + out.s16 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const int32_t& key, ss_plugin_state_data& out) +{ + out.s32 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const int64_t& key, ss_plugin_state_data& out) +{ + out.s64 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const uint8_t& key, ss_plugin_state_data& out) +{ + out.u8 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const uint16_t& key, ss_plugin_state_data& out) +{ + out.u16 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const uint32_t& key, ss_plugin_state_data& out) +{ + out.u32 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const uint64_t& key, ss_plugin_state_data& out) +{ + out.u64 = key; +} + +template<> void plugin_table_wrapper::get_key_data(const std::string& key, ss_plugin_state_data& out) +{ + out.str = key.c_str(); +} + +template<> void plugin_table_wrapper::get_key_data(const bool& key, ss_plugin_state_data& out) +{ + out.b = key; +} + // wraps instances of libsinsp::state::table and makes them comply // to the plugin API state tables definitions. struct sinsp_table_wrapper @@ -190,7 +512,7 @@ struct sinsp_table_wrapper if (pt) { m_table_plugin_owner = pt->m_owner; - m_table_plugin_input = &pt->m_input; + m_table_plugin_input = pt->m_input.get(); } } @@ -302,7 +624,7 @@ struct sinsp_table_wrapper // 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"); + throw sinsp_exception("field is defined as both static and dynamic: " + std::string(name)); } }); @@ -369,6 +691,12 @@ struct sinsp_table_wrapper return ret; } + 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); \ @@ -450,11 +778,6 @@ struct sinsp_table_wrapper return NULL; } - template static inline void convert_types(const From& from, To& to) - { - to = from; - } - 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); static ss_plugin_rc clear(ss_plugin_table_t* _t) @@ -596,12 +919,6 @@ struct sinsp_table_wrapper 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); }; -// special case for strings -template<> inline void sinsp_table_wrapper::convert_types(const std::string& from, const char*& to) -{ - to = from.c_str(); -} - 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) { auto t = static_cast(_t); @@ -624,7 +941,9 @@ ss_plugin_rc sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, ss_plu if (a->dynamic) \ { \ auto aa = static_cast*>(a->accessor); \ - convert_types(e->get_dynamic_field(*aa), out->_dtype); \ + _type _v; \ + e->get_dynamic_field(*aa, _v); \ + convert_types(_v, out->_dtype); \ } \ else \ { \ @@ -662,12 +981,12 @@ ss_plugin_rc sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, ss_pl if (a->dynamic) \ { \ auto aa = static_cast*>(a->accessor); \ - e->set_dynamic_field(*aa, (_type) in->_dtype); \ + e->set_dynamic_field<_type>(*aa, reinterpret_cast(in->_dtype)); \ } \ else \ { \ auto aa = static_cast*>(a->accessor); \ - e->set_static_field(*aa, (_type) in->_dtype); \ + e->set_static_field<_type>(*aa, in->_dtype); \ } \ return SS_PLUGIN_SUCCESS; \ } From 7db315865b6d943886db56514f8406b5ad80ca32 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Wed, 19 Jul 2023 15:45:39 +0000 Subject: [PATCH 17/66] test(userspace/libsinsp): stress bidirectional table access of sinsp and plugins Signed-off-by: Jason Dellaluce --- userspace/libsinsp/test/plugins.ut.cpp | 121 ++++++ .../libsinsp/test/plugins/sample_table.h | 30 +- userspace/libsinsp/test/plugins/tables.cpp | 374 ++++++++++++++++++ .../libsinsp/test/plugins/test_plugins.h | 1 + 4 files changed, 524 insertions(+), 2 deletions(-) create mode 100644 userspace/libsinsp/test/plugins/tables.cpp diff --git a/userspace/libsinsp/test/plugins.ut.cpp b/userspace/libsinsp/test/plugins.ut.cpp index 28fadd583e..a0adb05c8d 100644 --- a/userspace/libsinsp/test/plugins.ut.cpp +++ b/userspace/libsinsp/test/plugins.ut.cpp @@ -422,3 +422,124 @@ TEST_F(sinsp_with_test_input, plugin_syscall_async) m_inspector.close(); ASSERT_EQ(count, max_count); } + +// Scenario we load a plugin that parses any event and plays with the +// thread table, by stressing all the operations supported. After that, we +// also play with the plugin's table from the inspector C++ interface. +// Basically, we are verifying that the sinsp <-> plugin tables access +// is bidirectional and consistent. +TEST_F(sinsp_with_test_input, plugin_tables) +{ + auto& reg = m_inspector.get_table_registry(); + + add_default_init_thread(); + + // the threads table is always present + ASSERT_EQ(reg->tables().size(), 1); + ASSERT_NE(reg->tables().find("threads"), reg->tables().end()); + + // make sure we see a new table when we register the plugin + ASSERT_EQ(reg->tables().find("plugin_sample"), reg->tables().end()); + ASSERT_EQ(reg->get_table("plugin_sample"), nullptr); + register_plugin(&m_inspector, get_plugin_api_sample_tables); + ASSERT_EQ(reg->tables().size(), 2); + ASSERT_NE(reg->tables().find("plugin_sample"), reg->tables().end()); + ASSERT_ANY_THROW(reg->get_table("plugin_sample")); // wrong key type + 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"); + 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); + + // 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()); + + // 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()); + + // we open a capture and iterate, so that we make sure that all + // the state operations keep working at every round of the loop + open_inspector(); + auto asyncname = "sampleasync"; + auto sample_plugin_evtdata = "hello world"; + auto max_iterations = 10000; + for (int i = 0; i < max_iterations; i++) + { + auto evt = add_event_advance_ts(increasing_ts(), 1, PPME_ASYNCEVENT_E, 3, (uint32_t) 0, asyncname, scap_const_sized_buffer{&sample_plugin_evtdata, strlen(sample_plugin_evtdata) + 1}); + ASSERT_EQ(evt->get_type(), PPME_ASYNCEVENT_E); + ASSERT_EQ(evt->get_source_idx(), 0); + } + + // 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(); + + 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); + + // 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, std::move(table->new_entry())); + ASSERT_NE(t, nullptr); + ASSERT_NE(table->get_entry(i), nullptr); + 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); + ASSERT_EQ(tmpu64, 0); + tmpu64 = 5; + t->set_dynamic_field(sfieldacc, tmpu64); + tmpu64 = 0; + t->get_dynamic_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); + ASSERT_EQ(tmpstr, ""); + tmpstr = "hello"; + t->set_dynamic_field(dfieldacc, tmpstr); + tmpstr = ""; + t->get_dynamic_field(dfieldacc, tmpstr); + ASSERT_EQ(tmpstr, "hello"); + } + + // the plugin API does not support this yet + ASSERT_ANY_THROW(table->foreach_entry([](auto& e) { return true; })); + + // erasing an unknown thread + ASSERT_EQ(table->erase_entry(max_iterations), false); + ASSERT_EQ(table->entries_count(), max_iterations); + + // erase one of the newly-created thread + ASSERT_EQ(table->erase_entry(0), true); + ASSERT_EQ(table->entries_count(), max_iterations - 1); + + // clear all + ASSERT_NO_THROW(table->clear_entries()); + ASSERT_EQ(table->entries_count(), 0); +} diff --git a/userspace/libsinsp/test/plugins/sample_table.h b/userspace/libsinsp/test/plugins/sample_table.h index ea909a59c5..df19c8edd9 100644 --- a/userspace/libsinsp/test/plugins/sample_table.h +++ b/userspace/libsinsp/test/plugins/sample_table.h @@ -43,6 +43,7 @@ class sample_table } private: std::vector data; + std::vector strings; friend class sample_table; }; @@ -116,13 +117,22 @@ class sample_table 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) { + auto t = static_cast(_t); auto e = static_cast(_e); auto f = size_t (_f) - 1; while (e->data.size() <= f) { e->data.push_back(new ss_plugin_state_data()); + e->strings.emplace_back(); + } + if (t->fields[f].field_type == SS_PLUGIN_ST_STRING) + { + out->str = e->strings[f].c_str(); + } + else + { + memcpy(out, e->data[f], sizeof(ss_plugin_state_data)); } - memcpy(out, e->data[f], sizeof(ss_plugin_state_data)); return SS_PLUGIN_SUCCESS; } @@ -151,6 +161,12 @@ class sample_table return static_cast(new sample_table::entry()); } + static void destroy_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) + { + auto e = static_cast(_e); + delete e; + } + 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); @@ -162,13 +178,22 @@ class sample_table 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) { + auto t = static_cast(_t); auto e = static_cast(_e); auto f = size_t (_f) - 1; while (e->data.size() <= f) { e->data.push_back(new ss_plugin_state_data()); + e->strings.emplace_back(); + } + if (t->fields[f].field_type == SS_PLUGIN_ST_STRING) + { + e->strings[f] = in->str; + } + else + { + memcpy(e->data[f], in, sizeof(ss_plugin_state_data)); } - memcpy(e->data[f], in, sizeof(ss_plugin_state_data)); return SS_PLUGIN_SUCCESS; } @@ -200,6 +225,7 @@ class sample_table ret->writer.clear_table = clear; ret->writer.erase_table_entry = erase_entry; ret->writer.create_table_entry = create_entry; + ret->writer.destroy_table_entry = destroy_entry; ret->writer.add_table_entry = add_entry; ret->writer.write_entry_field = write_entry_field; return ret; diff --git a/userspace/libsinsp/test/plugins/tables.cpp b/userspace/libsinsp/test/plugins/tables.cpp new file mode 100644 index 0000000000..13cff5b696 --- /dev/null +++ b/userspace/libsinsp/test/plugins/tables.cpp @@ -0,0 +1,374 @@ +/* +Copyright (C) 2023 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 +#include +#include + +#include +#include "sample_table.h" +#include "test_plugins.h" + +/** + * Example of plugin that accesses the thread table and that exposes its own + * sta table. The goal is to test all the methods of the table API. + */ +typedef struct plugin_state +{ + std::string lasterr; + ss_plugin_table_t* thread_table; + ss_plugin_table_field_t* thread_static_field; + ss_plugin_table_field_t* thread_dynamic_field; + sample_table::ptr_t internal_table; + ss_plugin_table_field_t* internal_dynamic_field; +} plugin_state; + +static const char* plugin_get_required_api_version() +{ + return PLUGIN_API_VERSION_STR; +} + +static const char* plugin_get_version() +{ + return "0.1.0"; +} + +static const char* plugin_get_name() +{ + return "sample_tables"; +} + +static const char* plugin_get_description() +{ + return "some desc"; +} + +static const char* plugin_get_contact() +{ + return "some contact"; +} + +static const char* plugin_get_parse_event_sources() +{ + return "[\"syscall\"]"; +} + +static uint16_t* plugin_get_parse_event_types(uint32_t* num_types) +{ + static uint16_t types[] = {}; + *num_types = 0; + return types; +} + +static ss_plugin_t* plugin_init(const ss_plugin_init_input* in, ss_plugin_rc* rc) +{ + *rc = SS_PLUGIN_SUCCESS; + plugin_state *ret = new plugin_state(); + + if (!in || !in->tables) + { + *rc = SS_PLUGIN_FAILURE; + ret->lasterr = "invalid config input"; + return ret; + } + + // get accessor for thread table + ret->thread_table = in->tables->get_table( + in->owner, "threads", ss_plugin_state_type::SS_PLUGIN_ST_INT64); + if (!ret->thread_table) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't access thread table"; + return ret; + } + + // get an existing field from thread table entries + // todo(jasondellaluce): add tests for fields of other types as well + ret->thread_static_field = in->tables->fields.get_table_field( + ret->thread_table, "comm", ss_plugin_state_type::SS_PLUGIN_ST_STRING); + if (!ret->thread_static_field) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't get static field in thread table"; + return ret; + } + + // define a new field in thread table entries + // todo(jasondellaluce): add tests for fields of other types as well + ret->thread_dynamic_field = in->tables->fields.add_table_field( + ret->thread_table, "some_new_dynamic_field", ss_plugin_state_type::SS_PLUGIN_ST_UINT64); + if (!ret->thread_dynamic_field) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't add dynamic field in thread table"; + return ret; + } + + // define a new table that keeps a counter for all events. + // todo(jasondellaluce): add tests for fields of other types as well + ret->internal_table = sample_table::create("plugin_sample", ret->lasterr); + ret->internal_dynamic_field = ret->internal_table->fields.add_table_field( + ret->internal_table->table, "u64_val", + ss_plugin_state_type::SS_PLUGIN_ST_UINT64); + if (!ret->internal_dynamic_field) + { + *rc = SS_PLUGIN_FAILURE; + ret->lasterr = "can't define internal table field"; + return ret; + } + + if (SS_PLUGIN_SUCCESS != in->tables->add_table(in->owner, ret->internal_table.get())) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't add internal table"; + return ret; + } + return ret; +} + +static void plugin_destroy(ss_plugin_t* s) +{ + delete ((plugin_state *) s); +} + +static const char* plugin_get_last_error(ss_plugin_t* s) +{ + return ((plugin_state *) s)->lasterr.c_str(); +} + +// parses events and keeps a count for each thread about the syscalls of the open family +static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_input *ev, const ss_plugin_event_parse_input* in) +{ + ss_plugin_state_data tmp; + plugin_state *ps = (plugin_state *) s; + + // get table name + if (strcmp("threads", in->table_reader.get_table_name(ps->thread_table))) + { + fprintf(stderr, "table_reader.get_table_name (1) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // check that the table contains only the init thread + auto size = in->table_reader.get_table_size(ps->thread_table); + if (size != 1) + { + fprintf(stderr, "table_reader.get_table_size (2) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // get the init thread and read its comm + tmp.s64 = 1; + auto thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (3) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (4) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("init", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (4) inconsistency\n"); + exit(1); + } + + // read-write dynamic field from existing thread + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (5) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 0) + { + fprintf(stderr, "table_reader.read_entry_field (5) inconsistency\n"); + exit(1); + } + tmp.u64 = 5; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (6) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (7) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (7) inconsistency\n"); + exit(1); + } + tmp.u64 = 0; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (8) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // get non-existing thread + tmp.s64 = 10000; + thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); + if (thread) + { + fprintf(stderr, "table_reader.get_table_entry (9) inconsistency\n"); + exit(1); + } + + // creating a destroying a thread without adding it to the table + thread = in->table_writer.create_table_entry(ps->thread_table); + if (!thread) + { + fprintf(stderr, "table_reader.create_table_entry (10) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + in->table_writer.destroy_table_entry(ps->thread_table, thread); + + // creating and adding a thread to the table + thread = in->table_writer.create_table_entry(ps->thread_table); + if (!thread) + { + fprintf(stderr, "table_reader.create_table_entry (11) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + tmp.s64 = 10000; + thread = in->table_writer.add_table_entry(ps->thread_table, &tmp, thread); + if (!thread) + { + fprintf(stderr, "table_reader.add_table_entry (12) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + size = in->table_reader.get_table_size(ps->thread_table); + if (size != 2) + { + fprintf(stderr, "table_reader.get_table_size (13) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // get newly-created thread + tmp.s64 = 10000; + thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (14) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // read and write from newly-created thread (static field) + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (15) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (15) inconsistency\n"); + exit(1); + } + tmp.str = "hello"; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (16) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (17) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("hello", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (17) inconsistency\n"); + exit(1); + } + + // read and write from newly-created thread (dynamic field) + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (18) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 0) + { + fprintf(stderr, "table_reader.read_entry_field (18) inconsistency\n"); + exit(1); + } + tmp.u64 = 5; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (19) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (20) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (20) inconsistency\n"); + exit(1); + } + + // erasing an unknown thread + tmp.s64 = 10; + if (SS_PLUGIN_SUCCESS == in->table_writer.erase_table_entry(ps->thread_table, &tmp)) + { + fprintf(stderr, "table_reader.erase_table_entry (21) inconsistency\n"); + exit(1); + } + + // erase newly-created thread + tmp.s64 = 10000; + if (SS_PLUGIN_SUCCESS != in->table_writer.erase_table_entry(ps->thread_table, &tmp)) + { + fprintf(stderr, "table_reader.erase_table_entry (22) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + size = in->table_reader.get_table_size(ps->thread_table); + if (size != 1) + { + fprintf(stderr, "table_reader.get_table_size (23) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + return SS_PLUGIN_SUCCESS; +} + +void get_plugin_api_sample_tables(plugin_api& out) +{ + memset(&out, 0, sizeof(plugin_api)); + out.get_required_api_version = plugin_get_required_api_version; + out.get_version = plugin_get_version; + out.get_description = plugin_get_description; + out.get_contact = plugin_get_contact; + out.get_name = plugin_get_name; + out.get_last_error = plugin_get_last_error; + out.init = plugin_init; + out.destroy = plugin_destroy; + out.get_parse_event_sources = plugin_get_parse_event_sources; + out.get_parse_event_types = plugin_get_parse_event_types; + out.parse_event = plugin_parse_event; +} diff --git a/userspace/libsinsp/test/plugins/test_plugins.h b/userspace/libsinsp/test/plugins/test_plugins.h index e3d3cddf8e..2086ba4024 100644 --- a/userspace/libsinsp/test/plugins/test_plugins.h +++ b/userspace/libsinsp/test/plugins/test_plugins.h @@ -24,3 +24,4 @@ void get_plugin_api_sample_syscall_parse(plugin_api& out); void get_plugin_api_sample_syscall_async(plugin_api& out); void get_plugin_api_sample_plugin_source(plugin_api& out); void get_plugin_api_sample_plugin_extract(plugin_api& out); +void get_plugin_api_sample_tables(plugin_api& out); From 981da005179681c2d2489ac4e74ab202d60cb792 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 20 Jul 2023 09:00:22 +0000 Subject: [PATCH 18/66] chore(userspace/libsinsp): fix spacing Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 110 ++++++++++++------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index ae59b04afd..a68d898c10 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -55,8 +55,8 @@ limitations under the License. static inline ss_plugin_state_type typeinfo_to_state_type(const libsinsp::state::typeinfo& i) { - switch(i.index()) - { + switch(i.index()) + { case libsinsp::state::typeinfo::index_t::PT_INT8: return ss_plugin_state_type::SS_PLUGIN_ST_INT8; case libsinsp::state::typeinfo::index_t::PT_INT16: @@ -77,9 +77,9 @@ static inline ss_plugin_state_type typeinfo_to_state_type(const libsinsp::state: return ss_plugin_state_type::SS_PLUGIN_ST_STRING; case libsinsp::state::typeinfo::index_t::PT_BOOL: return ss_plugin_state_type::SS_PLUGIN_ST_BOOL; - default: - throw sinsp_exception("can't convert typeinfo to plugin state type: " + std::string(i.name())); - } + default: + throw sinsp_exception("can't convert typeinfo to plugin state type: " + std::string(i.name())); + } } template static inline void convert_types(const From& from, To& to) @@ -96,7 +96,7 @@ template<> inline void convert_types(const char* const& from, std::string& to) { if (!from || *from == '\0') { - to = ""; + to.clear(); } else { @@ -119,10 +119,10 @@ struct plugin_table_wrapper: public libsinsp::state::table const sinsp_plugin* o, const std::shared_ptr& 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; + 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; @@ -130,7 +130,7 @@ struct plugin_table_wrapper: public libsinsp::state::table std::vector m_accessors; virtual const std::unordered_map& fields() override - { + { uint32_t nfields = 0; auto res = m_input->fields.list_table_fields(m_input->table, &nfields); if (res == NULL) @@ -153,7 +153,7 @@ struct plugin_table_wrapper: public libsinsp::state::table } // make sure we have accessors for all of these fields - const auto& ret = ds::field_infos::fields(); + const auto& ret = ds::field_infos::fields(); for (const auto& it : ret) { const auto& f = it.second; @@ -172,7 +172,7 @@ struct plugin_table_wrapper: public libsinsp::state::table } } return ret; - } + } virtual const ds::field_info& add_field(const ds::field_info& field) override { @@ -217,7 +217,7 @@ struct plugin_table_wrapper: public libsinsp::state::table m_entry = o.m_entry; m_destroy_entry = o.m_destroy_entry; }; - plugin_table_entry& operator = (const plugin_table_entry& o) + plugin_table_entry& operator = (const plugin_table_entry& o) { ASSERT(!o.m_destroy_entry); if (o.m_destroy_entry) @@ -229,7 +229,7 @@ struct plugin_table_wrapper: public libsinsp::state::table m_entry = o.m_entry; m_destroy_entry = o.m_destroy_entry; } - plugin_table_entry(plugin_table_entry&& o) + plugin_table_entry(plugin_table_entry&& o) { m_owner = o.m_owner; m_input = o.m_input; @@ -237,7 +237,7 @@ struct plugin_table_wrapper: public libsinsp::state::table m_destroy_entry = o.m_destroy_entry; o.m_destroy_entry = false; }; - plugin_table_entry& operator = (plugin_table_entry&& o) + plugin_table_entry& operator = (plugin_table_entry&& o) { m_owner = o.m_owner; m_input = o.m_input; @@ -321,24 +321,24 @@ struct plugin_table_wrapper: public libsinsp::state::table }; plugin_table_wrapper(const sinsp_plugin* o, const ss_plugin_table_input* i) - : libsinsp::state::table(i->name, ss::field_infos()), + : libsinsp::state::table(i->name, ss::field_infos()), m_owner(o), m_input(std::make_shared(*i)), m_static_fields(), m_dyn_fields(std::make_shared(o, m_input)) - { - auto t = libsinsp::state::typeinfo::of(); - if (m_input->key_type != typeinfo_to_state_type(t)) - { - throw sinsp_exception("invalid key type for plugin-owned table: " + std::string(t.name())); - } - } - - virtual ~plugin_table_wrapper() = default; - plugin_table_wrapper(plugin_table_wrapper&&) = default; - plugin_table_wrapper& operator = (plugin_table_wrapper&&) = default; - plugin_table_wrapper(const plugin_table_wrapper& s) = delete; - plugin_table_wrapper& operator = (const plugin_table_wrapper& s) = delete; + { + auto t = libsinsp::state::typeinfo::of(); + if (m_input->key_type != typeinfo_to_state_type(t)) + { + throw sinsp_exception("invalid key type for plugin-owned table: " + std::string(t.name())); + } + } + + virtual ~plugin_table_wrapper() = default; + plugin_table_wrapper(plugin_table_wrapper&&) = default; + plugin_table_wrapper& operator = (plugin_table_wrapper&&) = default; + plugin_table_wrapper(const plugin_table_wrapper& s) = delete; + plugin_table_wrapper& operator = (const plugin_table_wrapper& s) = delete; const sinsp_plugin* m_owner; std::shared_ptr m_input; @@ -351,16 +351,16 @@ struct plugin_table_wrapper: public libsinsp::state::table } 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 m_static_fields; - } + return m_static_fields; + } - std::shared_ptr dynamic_fields() const override - { - return m_dyn_fields; - } + std::shared_ptr dynamic_fields() const override + { + return m_dyn_fields; + } size_t entries_count() const override { @@ -386,7 +386,7 @@ struct plugin_table_wrapper: public libsinsp::state::table throw sinsp_exception(invalid_access_msg("foreach")); } - std::unique_ptr new_entry() const override + std::unique_ptr new_entry() const override { auto res = m_input->writer.create_table_entry(m_input->table); if (res == NULL) @@ -408,7 +408,7 @@ struct plugin_table_wrapper: public libsinsp::state::table return std::shared_ptr(new plugin_table_entry(m_owner, m_input, m_dyn_fields, res, false)); } - std::shared_ptr add_entry(const KeyType& key, std::unique_ptr e) override + std::shared_ptr add_entry(const KeyType& key, std::unique_ptr e) override { ASSERT(dynamic_cast(e.get()) != nullptr); plugin_table_entry* entry = static_cast(e.get()); @@ -424,7 +424,7 @@ struct plugin_table_wrapper: public libsinsp::state::table return std::move(e); } - bool erase_entry(const KeyType& key) override + bool erase_entry(const KeyType& key) override { ss_plugin_state_data keydata; get_key_data(key, keydata); @@ -491,16 +491,16 @@ template<> void plugin_table_wrapper::get_key_data(const bool& key, ss_plu struct sinsp_table_wrapper { // wraps a dynamic or a static field accessor and its type - struct field_accessor_wrapper - { + struct field_accessor_wrapper + { void* accessor; bool dynamic; ss_plugin_state_type data_type; - }; + }; 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_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) { // note: if the we're wrapping a plugin-implemented table under the hood, @@ -516,11 +516,11 @@ struct 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; + 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() { for (auto& acc : m_field_accessors) @@ -546,10 +546,10 @@ struct sinsp_table_wrapper } 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; + 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; @@ -1126,8 +1126,8 @@ ss_plugin_table_info* sinsp_plugin::table_api_list_tables(ss_plugin_owner_t* o, void sinsp_plugin::table_input_deleter::operator()(ss_plugin_table_input* in) { - delete static_cast(in->table); - delete in; + delete static_cast(in->table); + 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) From c64b5c6d64303909b3be50a994106d63414ed91d Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 20 Jul 2023 10:18:13 +0000 Subject: [PATCH 19/66] chore(userspace/libsinsp): cosmetic changes and docs Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 157 +++--- userspace/libsinsp/test/plugins.ut.cpp | 4 +- userspace/libsinsp/test/plugins/tables.cpp | 568 ++++++++++----------- 3 files changed, 382 insertions(+), 347 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index a68d898c10..9d1583e27c 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -92,6 +92,7 @@ template<> inline void convert_types(const std::string& from, const char*& to) { to = from.c_str(); } + template<> inline void convert_types(const char* const& from, std::string& to) { if (!from || *from == '\0') @@ -104,6 +105,11 @@ template<> inline void convert_types(const char* const& from, std::string& to) } } +static inline std::string table_input_error_prefix(const sinsp_plugin* o, ss_plugin_table_input* i) +{ + return "error in state table '" + std::string(i->name) + "' defined by plugin '" + o->name() + "': "; +} + // wraps instances of ss_plugin_table_input and makes them comply // to the libsinsp::state::table state tables definition. template @@ -131,12 +137,20 @@ struct plugin_table_wrapper: public libsinsp::state::table virtual const std::unordered_map& fields() override { + // list all the fields of the plugin table uint32_t nfields = 0; auto res = m_input->fields.list_table_fields(m_input->table, &nfields); if (res == NULL) { - throw sinsp_exception("plugin table list fields error: " + m_owner->get_last_error()); + 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++) @@ -152,7 +166,12 @@ struct plugin_table_wrapper: public libsinsp::state::table } } - // make sure we have accessors for all of these fields + // 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) { @@ -166,7 +185,7 @@ struct plugin_table_wrapper: public libsinsp::state::table auto facc = m_input->fields.get_table_field(m_input->table, f.name().c_str(), typeinfo_to_state_type(f.info())); if (facc == NULL) { - throw sinsp_exception("plugin table get field error: " + m_owner->get_last_error()); + 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; } @@ -179,10 +198,18 @@ struct plugin_table_wrapper: public libsinsp::state::table auto ret = m_input->fields.add_table_field(m_input->table, field.name().c_str(), typeinfo_to_state_type(field.info())); if (ret == NULL) { - throw sinsp_exception("plugin table list fields error: " + m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "add table field failure: " + m_owner->get_last_error()); } - // trigger all updates and obtain the right ref + + // 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(field); } }; @@ -194,61 +221,64 @@ struct plugin_table_wrapper: public libsinsp::state::table const std::shared_ptr& i, const std::shared_ptr& fields, ss_plugin_table_entry_t* e, - bool destroy) + bool detached) : table_entry(fields), m_owner(o), m_input(i), m_entry(e), - m_destroy_entry(destroy) {}; + m_detached(detached) {}; plugin_table_entry(const plugin_table_entry& o) { - // note: this is not supposed to ever happen: in the general case, - // m_destroy_entry will be false because plugin_table_entry will not - // own the entry (the table will). The only time in which it is owner - // is when being held in a unique pointer during the - // new_entry -> add_entry flow. - ASSERT(!o.m_destroy_entry); - if (o.m_destroy_entry) + // note: this is not supposed to ever happen. In the general case + // m_detached will be false because plugin_table_entry will not + // own the entry (it will be held by the table itself). + // The only time in which it is owner is when being held in a + // unique pointer during the new_entry -> add_entry/destroy_entry flow. + ASSERT(!o.m_detached); + if (o.m_detached) { - throw sinsp_exception("plugin_table_entry can't be copied while being owner of an entry pointer"); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "entry can't be copied while being detached"); } m_owner = o.m_owner; m_input = o.m_input; m_entry = o.m_entry; - m_destroy_entry = o.m_destroy_entry; + m_detached = o.m_detached; }; plugin_table_entry& operator = (const plugin_table_entry& o) { - ASSERT(!o.m_destroy_entry); - if (o.m_destroy_entry) + ASSERT(!o.m_detached); + if (o.m_detached) { - throw sinsp_exception("plugin_table_entry can't be copied while being owner of an entry pointer"); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "entry can't be copied while being detached"); } m_owner = o.m_owner; m_input = o.m_input; m_entry = o.m_entry; - m_destroy_entry = o.m_destroy_entry; + m_detached = o.m_detached; } plugin_table_entry(plugin_table_entry&& o) { m_owner = o.m_owner; m_input = o.m_input; m_entry = o.m_entry; - m_destroy_entry = o.m_destroy_entry; - o.m_destroy_entry = false; + m_detached = o.m_detached; + o.m_detached = false; }; plugin_table_entry& operator = (plugin_table_entry&& o) { m_owner = o.m_owner; m_input = o.m_input; m_entry = o.m_entry; - m_destroy_entry = o.m_destroy_entry; - o.m_destroy_entry = false; + m_detached = o.m_detached; + o.m_detached = false; return *this; }; virtual ~plugin_table_entry() { - if (m_destroy_entry) + // if this gets destroyed when holding a detached entry pointer + // (one that is allocated, but not yet inserted in the table), + // then it must be destroyed here. + if (m_detached) { m_input->writer.destroy_table_entry(m_input->table, m_entry); } @@ -257,17 +287,17 @@ struct plugin_table_wrapper: public libsinsp::state::table const sinsp_plugin* m_owner; std::shared_ptr m_input; ss_plugin_table_entry_t* m_entry; - bool m_destroy_entry; + bool m_detached; // note(jasondellaluce): dynamic cast is expensive but this is not expected - // to ever be invoked because we plan of settings the fields shared pointer + // 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("plugin table can only be set with plugin dynamic fields"); + 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); } @@ -279,7 +309,7 @@ struct plugin_table_wrapper: public libsinsp::state::table auto rc = m_input->reader.read_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &dout); if (rc != SS_PLUGIN_SUCCESS) { - throw sinsp_exception("plugin table entry read field error: " + m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "read field failure: " + m_owner->get_last_error()); } #define _X(_type, _dtype) \ { \ @@ -304,7 +334,7 @@ struct plugin_table_wrapper: public libsinsp::state::table auto rc = m_input->writer.write_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &v); if (rc != SS_PLUGIN_SUCCESS) { - throw sinsp_exception("plugin table entry write field error: " + m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "write field failure: " + m_owner->get_last_error()); } } private: @@ -312,10 +342,11 @@ struct plugin_table_wrapper: public libsinsp::state::table { if (dynamic_fields() == nullptr) { - throw sinsp_exception("plugin table entry fields definitions are not set"); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "local fields definitions not set"); } - // note: casting is safe because we force the plugin_field_infos - // subtype both the constructor and the setter + // 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()); } }; @@ -330,7 +361,7 @@ struct plugin_table_wrapper: public libsinsp::state::table auto t = libsinsp::state::typeinfo::of(); if (m_input->key_type != typeinfo_to_state_type(t)) { - throw sinsp_exception("invalid key type for plugin-owned table: " + std::string(t.name())); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "invalid key type: " + std::string(t.name())); } } @@ -345,11 +376,6 @@ struct plugin_table_wrapper: public libsinsp::state::table libsinsp::state::static_struct::field_infos m_static_fields; std::shared_ptr m_dyn_fields; - inline std::string invalid_access_msg(const std::string& op) const - { - return "operation '" + op + "' not supported by plugin-owned table '" + this->name() + "'"; - } - const libsinsp::state::static_struct::field_infos& static_fields() const override { // note: always empty, plugin-defined table have no "static" fields, @@ -367,7 +393,7 @@ struct plugin_table_wrapper: public libsinsp::state::table auto res = m_input->reader.get_table_size(m_input->table); if (res == (uint64_t) -1) { - throw sinsp_exception(m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "get size failure: " + m_owner->get_last_error()); } return (size_t) res; } @@ -377,13 +403,13 @@ struct plugin_table_wrapper: public libsinsp::state::table auto res = m_input->writer.clear_table(m_input->table); if (res != SS_PLUGIN_SUCCESS) { - throw sinsp_exception(m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "clear entries failure: " + m_owner->get_last_error()); } } bool foreach_entry(std::function pred) override { - throw sinsp_exception(invalid_access_msg("foreach")); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "foreach operation not supported by plugin api"); } std::unique_ptr new_entry() const override @@ -391,7 +417,7 @@ struct plugin_table_wrapper: public libsinsp::state::table auto res = m_input->writer.create_table_entry(m_input->table); if (res == NULL) { - throw sinsp_exception(m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "create entry failure: " + m_owner->get_last_error()); } return std::unique_ptr(new plugin_table_entry(m_owner, m_input, m_dyn_fields, res, true)); } @@ -399,10 +425,12 @@ struct plugin_table_wrapper: public libsinsp::state::table std::shared_ptr get_entry(const KeyType& key) override { ss_plugin_state_data keydata; - get_key_data(key, keydata); + get_key_as_data(key, keydata); auto res = m_input->reader.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; } return std::shared_ptr(new plugin_table_entry(m_owner, m_input, m_dyn_fields, res, false)); @@ -410,78 +438,85 @@ struct plugin_table_wrapper: public libsinsp::state::table std::shared_ptr add_entry(const KeyType& key, std::unique_ptr e) override { + // 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()); + ss_plugin_state_data keydata; - get_key_data(key, keydata); + get_key_as_data(key, keydata); auto res = m_input->writer.add_table_entry(m_input->table, &keydata, entry->m_entry); if (res == NULL) { - throw sinsp_exception(m_owner->get_last_error()); + 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_destroy_entry = false; + entry->m_detached = false; return std::move(e); } bool erase_entry(const KeyType& key) override { ss_plugin_state_data keydata; - get_key_data(key, keydata); + get_key_as_data(key, keydata); auto res = m_input->writer.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: - void get_key_data(const KeyType& key, ss_plugin_state_data& out); + static void get_key_as_data(const KeyType& key, ss_plugin_state_data& out); }; -template<> void plugin_table_wrapper::get_key_data(const int8_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const int8_t& key, ss_plugin_state_data& out) { out.s8 = key; } -template<> void plugin_table_wrapper::get_key_data(const int16_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const int16_t& key, ss_plugin_state_data& out) { out.s16 = key; } -template<> void plugin_table_wrapper::get_key_data(const int32_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const int32_t& key, ss_plugin_state_data& out) { out.s32 = key; } -template<> void plugin_table_wrapper::get_key_data(const int64_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const int64_t& key, ss_plugin_state_data& out) { out.s64 = key; } -template<> void plugin_table_wrapper::get_key_data(const uint8_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const uint8_t& key, ss_plugin_state_data& out) { out.u8 = key; } -template<> void plugin_table_wrapper::get_key_data(const uint16_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const uint16_t& key, ss_plugin_state_data& out) { out.u16 = key; } -template<> void plugin_table_wrapper::get_key_data(const uint32_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const uint32_t& key, ss_plugin_state_data& out) { out.u32 = key; } -template<> void plugin_table_wrapper::get_key_data(const uint64_t& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const uint64_t& key, ss_plugin_state_data& out) { out.u64 = key; } -template<> void plugin_table_wrapper::get_key_data(const std::string& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const std::string& key, ss_plugin_state_data& out) { out.str = key.c_str(); } -template<> void plugin_table_wrapper::get_key_data(const bool& key, ss_plugin_state_data& out) +template<> void plugin_table_wrapper::get_key_as_data(const bool& key, ss_plugin_state_data& out) { out.b = key; } @@ -643,7 +678,7 @@ struct sinsp_table_wrapper { if (data_type != typeinfo_to_state_type(fixed_it->second.info())) { - throw sinsp_exception("incompatible data types for field: " + std::string(name)); + throw sinsp_exception("incompatible data types for static field: " + std::string(name)); } __PLUGIN_STATETYPE_SWITCH(data_type); } @@ -665,7 +700,7 @@ struct sinsp_table_wrapper { if (data_type != typeinfo_to_state_type(dyn_it->second.info())) { - throw sinsp_exception("incompatible data types for field: " + std::string(name)); + throw sinsp_exception("incompatible data types for dynamic field: " + std::string(name)); } __PLUGIN_STATETYPE_SWITCH(data_type); } diff --git a/userspace/libsinsp/test/plugins.ut.cpp b/userspace/libsinsp/test/plugins.ut.cpp index a0adb05c8d..5d55dae2ce 100644 --- a/userspace/libsinsp/test/plugins.ut.cpp +++ b/userspace/libsinsp/test/plugins.ut.cpp @@ -479,8 +479,8 @@ TEST_F(sinsp_with_test_input, plugin_tables) open_inspector(); auto asyncname = "sampleasync"; auto sample_plugin_evtdata = "hello world"; - auto max_iterations = 10000; - for (int i = 0; i < max_iterations; i++) + uint64_t max_iterations = 10000; + for (uint64_t i = 0; i < max_iterations; i++) { auto evt = add_event_advance_ts(increasing_ts(), 1, PPME_ASYNCEVENT_E, 3, (uint32_t) 0, asyncname, scap_const_sized_buffer{&sample_plugin_evtdata, strlen(sample_plugin_evtdata) + 1}); ASSERT_EQ(evt->get_type(), PPME_ASYNCEVENT_E); diff --git a/userspace/libsinsp/test/plugins/tables.cpp b/userspace/libsinsp/test/plugins/tables.cpp index 13cff5b696..cabbfeb233 100644 --- a/userspace/libsinsp/test/plugins/tables.cpp +++ b/userspace/libsinsp/test/plugins/tables.cpp @@ -29,337 +29,337 @@ limitations under the License. */ typedef struct plugin_state { - std::string lasterr; - ss_plugin_table_t* thread_table; - ss_plugin_table_field_t* thread_static_field; - ss_plugin_table_field_t* thread_dynamic_field; - sample_table::ptr_t internal_table; - ss_plugin_table_field_t* internal_dynamic_field; + std::string lasterr; + ss_plugin_table_t* thread_table; + ss_plugin_table_field_t* thread_static_field; + ss_plugin_table_field_t* thread_dynamic_field; + sample_table::ptr_t internal_table; + ss_plugin_table_field_t* internal_dynamic_field; } plugin_state; static const char* plugin_get_required_api_version() { - return PLUGIN_API_VERSION_STR; + return PLUGIN_API_VERSION_STR; } static const char* plugin_get_version() { - return "0.1.0"; + return "0.1.0"; } static const char* plugin_get_name() { - return "sample_tables"; + return "sample_tables"; } static const char* plugin_get_description() { - return "some desc"; + return "some desc"; } static const char* plugin_get_contact() { - return "some contact"; + return "some contact"; } static const char* plugin_get_parse_event_sources() { - return "[\"syscall\"]"; + return "[\"syscall\"]"; } static uint16_t* plugin_get_parse_event_types(uint32_t* num_types) { - static uint16_t types[] = {}; - *num_types = 0; - return types; + static uint16_t types[] = {}; + *num_types = 0; + return types; } static ss_plugin_t* plugin_init(const ss_plugin_init_input* in, ss_plugin_rc* rc) { - *rc = SS_PLUGIN_SUCCESS; - plugin_state *ret = new plugin_state(); - - if (!in || !in->tables) - { - *rc = SS_PLUGIN_FAILURE; - ret->lasterr = "invalid config input"; - return ret; - } - - // get accessor for thread table - ret->thread_table = in->tables->get_table( - in->owner, "threads", ss_plugin_state_type::SS_PLUGIN_ST_INT64); - if (!ret->thread_table) - { - *rc = SS_PLUGIN_FAILURE; - auto err = in->get_owner_last_error(in->owner); - ret->lasterr = err ? err : "can't access thread table"; - return ret; - } - - // get an existing field from thread table entries - // todo(jasondellaluce): add tests for fields of other types as well - ret->thread_static_field = in->tables->fields.get_table_field( - ret->thread_table, "comm", ss_plugin_state_type::SS_PLUGIN_ST_STRING); - if (!ret->thread_static_field) - { - *rc = SS_PLUGIN_FAILURE; - auto err = in->get_owner_last_error(in->owner); - ret->lasterr = err ? err : "can't get static field in thread table"; - return ret; - } - - // define a new field in thread table entries - // todo(jasondellaluce): add tests for fields of other types as well - ret->thread_dynamic_field = in->tables->fields.add_table_field( - ret->thread_table, "some_new_dynamic_field", ss_plugin_state_type::SS_PLUGIN_ST_UINT64); - if (!ret->thread_dynamic_field) - { - *rc = SS_PLUGIN_FAILURE; - auto err = in->get_owner_last_error(in->owner); - ret->lasterr = err ? err : "can't add dynamic field in thread table"; - return ret; - } - - // define a new table that keeps a counter for all events. - // todo(jasondellaluce): add tests for fields of other types as well - ret->internal_table = sample_table::create("plugin_sample", ret->lasterr); - ret->internal_dynamic_field = ret->internal_table->fields.add_table_field( - ret->internal_table->table, "u64_val", - ss_plugin_state_type::SS_PLUGIN_ST_UINT64); - if (!ret->internal_dynamic_field) - { - *rc = SS_PLUGIN_FAILURE; - ret->lasterr = "can't define internal table field"; - return ret; - } - - if (SS_PLUGIN_SUCCESS != in->tables->add_table(in->owner, ret->internal_table.get())) - { - *rc = SS_PLUGIN_FAILURE; - auto err = in->get_owner_last_error(in->owner); - ret->lasterr = err ? err : "can't add internal table"; - return ret; - } - return ret; + *rc = SS_PLUGIN_SUCCESS; + plugin_state *ret = new plugin_state(); + + if (!in || !in->tables) + { + *rc = SS_PLUGIN_FAILURE; + ret->lasterr = "invalid config input"; + return ret; + } + + // get accessor for thread table + ret->thread_table = in->tables->get_table( + in->owner, "threads", ss_plugin_state_type::SS_PLUGIN_ST_INT64); + if (!ret->thread_table) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't access thread table"; + return ret; + } + + // get an existing field from thread table entries + // todo(jasondellaluce): add tests for fields of other types as well + ret->thread_static_field = in->tables->fields.get_table_field( + ret->thread_table, "comm", ss_plugin_state_type::SS_PLUGIN_ST_STRING); + if (!ret->thread_static_field) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't get static field in thread table"; + return ret; + } + + // define a new field in thread table entries + // todo(jasondellaluce): add tests for fields of other types as well + ret->thread_dynamic_field = in->tables->fields.add_table_field( + ret->thread_table, "some_new_dynamic_field", ss_plugin_state_type::SS_PLUGIN_ST_UINT64); + if (!ret->thread_dynamic_field) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't add dynamic field in thread table"; + return ret; + } + + // define a new table that keeps a counter for all events. + // todo(jasondellaluce): add tests for fields of other types as well + ret->internal_table = sample_table::create("plugin_sample", ret->lasterr); + ret->internal_dynamic_field = ret->internal_table->fields.add_table_field( + ret->internal_table->table, "u64_val", + ss_plugin_state_type::SS_PLUGIN_ST_UINT64); + if (!ret->internal_dynamic_field) + { + *rc = SS_PLUGIN_FAILURE; + ret->lasterr = "can't define internal table field"; + return ret; + } + + if (SS_PLUGIN_SUCCESS != in->tables->add_table(in->owner, ret->internal_table.get())) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't add internal table"; + return ret; + } + return ret; } static void plugin_destroy(ss_plugin_t* s) { - delete ((plugin_state *) s); + delete ((plugin_state *) s); } static const char* plugin_get_last_error(ss_plugin_t* s) { - return ((plugin_state *) s)->lasterr.c_str(); + return ((plugin_state *) s)->lasterr.c_str(); } // parses events and keeps a count for each thread about the syscalls of the open family static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_input *ev, const ss_plugin_event_parse_input* in) { - ss_plugin_state_data tmp; - plugin_state *ps = (plugin_state *) s; - - // get table name - if (strcmp("threads", in->table_reader.get_table_name(ps->thread_table))) - { - fprintf(stderr, "table_reader.get_table_name (1) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - // check that the table contains only the init thread - auto size = in->table_reader.get_table_size(ps->thread_table); - if (size != 1) - { - fprintf(stderr, "table_reader.get_table_size (2) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - // get the init thread and read its comm - tmp.s64 = 1; - auto thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); - if (!thread) - { - fprintf(stderr, "table_reader.get_table_entry (3) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } + ss_plugin_state_data tmp; + plugin_state *ps = (plugin_state *) s; + + // get table name + if (strcmp("threads", in->table_reader.get_table_name(ps->thread_table))) + { + fprintf(stderr, "table_reader.get_table_name (1) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // check that the table contains only the init thread + auto size = in->table_reader.get_table_size(ps->thread_table); + if (size != 1) + { + fprintf(stderr, "table_reader.get_table_size (2) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // get the init thread and read its comm + tmp.s64 = 1; + auto thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (3) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (4) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("init", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (4) inconsistency\n"); - exit(1); - } - - // read-write dynamic field from existing thread - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (5) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 0) - { - fprintf(stderr, "table_reader.read_entry_field (5) inconsistency\n"); - exit(1); - } - tmp.u64 = 5; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (6) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (7) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 5) - { - fprintf(stderr, "table_reader.read_entry_field (7) inconsistency\n"); - exit(1); - } - tmp.u64 = 0; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (8) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - // get non-existing thread - tmp.s64 = 10000; - thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); - if (thread) - { - fprintf(stderr, "table_reader.get_table_entry (9) inconsistency\n"); - exit(1); - } - - // creating a destroying a thread without adding it to the table - thread = in->table_writer.create_table_entry(ps->thread_table); - if (!thread) - { - fprintf(stderr, "table_reader.create_table_entry (10) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - in->table_writer.destroy_table_entry(ps->thread_table, thread); - - // creating and adding a thread to the table - thread = in->table_writer.create_table_entry(ps->thread_table); - if (!thread) - { - fprintf(stderr, "table_reader.create_table_entry (11) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - tmp.s64 = 10000; - thread = in->table_writer.add_table_entry(ps->thread_table, &tmp, thread); - if (!thread) - { - fprintf(stderr, "table_reader.add_table_entry (12) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - size = in->table_reader.get_table_size(ps->thread_table); - if (size != 2) - { - fprintf(stderr, "table_reader.get_table_size (13) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - // get newly-created thread - tmp.s64 = 10000; - thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); - if (!thread) - { - fprintf(stderr, "table_reader.get_table_entry (14) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - // read and write from newly-created thread (static field) - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (15) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (15) inconsistency\n"); - exit(1); - } - tmp.str = "hello"; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (16) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (17) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("hello", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (17) inconsistency\n"); - exit(1); - } - - // read and write from newly-created thread (dynamic field) - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (18) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 0) - { - fprintf(stderr, "table_reader.read_entry_field (18) inconsistency\n"); - exit(1); - } - tmp.u64 = 5; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (19) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (20) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 5) - { - fprintf(stderr, "table_reader.read_entry_field (20) inconsistency\n"); - exit(1); - } - - // erasing an unknown thread - tmp.s64 = 10; - if (SS_PLUGIN_SUCCESS == in->table_writer.erase_table_entry(ps->thread_table, &tmp)) - { - fprintf(stderr, "table_reader.erase_table_entry (21) inconsistency\n"); - exit(1); - } - - // erase newly-created thread - tmp.s64 = 10000; - if (SS_PLUGIN_SUCCESS != in->table_writer.erase_table_entry(ps->thread_table, &tmp)) - { - fprintf(stderr, "table_reader.erase_table_entry (22) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - size = in->table_reader.get_table_size(ps->thread_table); - if (size != 1) - { - fprintf(stderr, "table_reader.get_table_size (23) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - return SS_PLUGIN_SUCCESS; + { + fprintf(stderr, "table_reader.read_entry_field (4) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("init", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (4) inconsistency\n"); + exit(1); + } + + // read-write dynamic field from existing thread + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (5) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 0) + { + fprintf(stderr, "table_reader.read_entry_field (5) inconsistency\n"); + exit(1); + } + tmp.u64 = 5; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (6) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (7) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (7) inconsistency\n"); + exit(1); + } + tmp.u64 = 0; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (8) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // get non-existing thread + tmp.s64 = 10000; + thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); + if (thread) + { + fprintf(stderr, "table_reader.get_table_entry (9) inconsistency\n"); + exit(1); + } + + // creating a destroying a thread without adding it to the table + thread = in->table_writer.create_table_entry(ps->thread_table); + if (!thread) + { + fprintf(stderr, "table_reader.create_table_entry (10) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + in->table_writer.destroy_table_entry(ps->thread_table, thread); + + // creating and adding a thread to the table + thread = in->table_writer.create_table_entry(ps->thread_table); + if (!thread) + { + fprintf(stderr, "table_reader.create_table_entry (11) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + tmp.s64 = 10000; + thread = in->table_writer.add_table_entry(ps->thread_table, &tmp, thread); + if (!thread) + { + fprintf(stderr, "table_reader.add_table_entry (12) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + size = in->table_reader.get_table_size(ps->thread_table); + if (size != 2) + { + fprintf(stderr, "table_reader.get_table_size (13) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // get newly-created thread + tmp.s64 = 10000; + thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (14) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + // read and write from newly-created thread (static field) + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (15) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (15) inconsistency\n"); + exit(1); + } + tmp.str = "hello"; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (16) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (17) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("hello", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (17) inconsistency\n"); + exit(1); + } + + // read and write from newly-created thread (dynamic field) + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (18) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 0) + { + fprintf(stderr, "table_reader.read_entry_field (18) inconsistency\n"); + exit(1); + } + tmp.u64 = 5; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (19) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (20) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (20) inconsistency\n"); + exit(1); + } + + // erasing an unknown thread + tmp.s64 = 10; + if (SS_PLUGIN_SUCCESS == in->table_writer.erase_table_entry(ps->thread_table, &tmp)) + { + fprintf(stderr, "table_reader.erase_table_entry (21) inconsistency\n"); + exit(1); + } + + // erase newly-created thread + tmp.s64 = 10000; + if (SS_PLUGIN_SUCCESS != in->table_writer.erase_table_entry(ps->thread_table, &tmp)) + { + fprintf(stderr, "table_reader.erase_table_entry (22) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + size = in->table_reader.get_table_size(ps->thread_table); + if (size != 1) + { + fprintf(stderr, "table_reader.get_table_size (23) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + + return SS_PLUGIN_SUCCESS; } void get_plugin_api_sample_tables(plugin_api& out) { - memset(&out, 0, sizeof(plugin_api)); + memset(&out, 0, sizeof(plugin_api)); out.get_required_api_version = plugin_get_required_api_version; out.get_version = plugin_get_version; out.get_description = plugin_get_description; @@ -368,7 +368,7 @@ void get_plugin_api_sample_tables(plugin_api& out) out.get_last_error = plugin_get_last_error; out.init = plugin_init; out.destroy = plugin_destroy; - out.get_parse_event_sources = plugin_get_parse_event_sources; - out.get_parse_event_types = plugin_get_parse_event_types; - out.parse_event = plugin_parse_event; + out.get_parse_event_sources = plugin_get_parse_event_sources; + out.get_parse_event_types = plugin_get_parse_event_types; + out.parse_event = plugin_parse_event; } From 5771fd3fd86169703b73e0594c1afe9c436f0ff4 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 20 Jul 2023 13:38:42 +0000 Subject: [PATCH 20/66] test(userspace/libsinsp): test dynamic string fields in plugins Signed-off-by: Jason Dellaluce --- userspace/libsinsp/test/plugins/tables.cpp | 82 +++++++++++++++++----- 1 file changed, 63 insertions(+), 19 deletions(-) diff --git a/userspace/libsinsp/test/plugins/tables.cpp b/userspace/libsinsp/test/plugins/tables.cpp index cabbfeb233..894dbd1c8e 100644 --- a/userspace/libsinsp/test/plugins/tables.cpp +++ b/userspace/libsinsp/test/plugins/tables.cpp @@ -33,6 +33,7 @@ typedef struct plugin_state ss_plugin_table_t* thread_table; ss_plugin_table_field_t* thread_static_field; ss_plugin_table_field_t* thread_dynamic_field; + ss_plugin_table_field_t* thread_dynamic_field_str; sample_table::ptr_t internal_table; ss_plugin_table_field_t* internal_dynamic_field; } plugin_state; @@ -120,6 +121,15 @@ static ss_plugin_t* plugin_init(const ss_plugin_init_input* in, ss_plugin_rc* rc ret->lasterr = err ? err : "can't add dynamic field in thread table"; return ret; } + ret->thread_dynamic_field_str = in->tables->fields.add_table_field( + ret->thread_table, "some_new_dynamic_field_str", ss_plugin_state_type::SS_PLUGIN_ST_STRING); + if (!ret->thread_dynamic_field_str) + { + *rc = SS_PLUGIN_FAILURE; + auto err = in->get_owner_last_error(in->owner); + ret->lasterr = err ? err : "can't add dynamic field in thread table"; + return ret; + } // define a new table that keeps a counter for all events. // todo(jasondellaluce): add tests for fields of other types as well @@ -228,12 +238,46 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp exit(1); } + // read-write dynamic field (str from existing thread + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (9) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (9) inconsistency\n"); + exit(1); + } + tmp.str = "hello"; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (10) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (11) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("hello", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (11) inconsistency\n"); + exit(1); + } + tmp.str = ""; + if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (12) failure: %s\n", in->get_owner_last_error(in->owner)); + exit(1); + } + // get non-existing thread tmp.s64 = 10000; thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); if (thread) { - fprintf(stderr, "table_reader.get_table_entry (9) inconsistency\n"); + fprintf(stderr, "table_reader.get_table_entry (13) inconsistency\n"); exit(1); } @@ -241,7 +285,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp thread = in->table_writer.create_table_entry(ps->thread_table); if (!thread) { - fprintf(stderr, "table_reader.create_table_entry (10) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.create_table_entry (14) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } in->table_writer.destroy_table_entry(ps->thread_table, thread); @@ -250,20 +294,20 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp thread = in->table_writer.create_table_entry(ps->thread_table); if (!thread) { - fprintf(stderr, "table_reader.create_table_entry (11) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.create_table_entry (15) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } tmp.s64 = 10000; thread = in->table_writer.add_table_entry(ps->thread_table, &tmp, thread); if (!thread) { - fprintf(stderr, "table_reader.add_table_entry (12) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.add_table_entry (16) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } size = in->table_reader.get_table_size(ps->thread_table); if (size != 2) { - fprintf(stderr, "table_reader.get_table_size (13) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.get_table_size (17) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } @@ -272,63 +316,63 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); if (!thread) { - fprintf(stderr, "table_reader.get_table_entry (14) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.get_table_entry (18) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } // read and write from newly-created thread (static field) if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) { - fprintf(stderr, "table_reader.read_entry_field (15) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.read_entry_field (19) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } if (strcmp("", tmp.str)) { - fprintf(stderr, "table_reader.read_entry_field (15) inconsistency\n"); + fprintf(stderr, "table_reader.read_entry_field (19) inconsistency\n"); exit(1); } tmp.str = "hello"; if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) { - fprintf(stderr, "table_reader.write_entry_field (16) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.write_entry_field (20) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) { - fprintf(stderr, "table_reader.read_entry_field (17) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.read_entry_field (21) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } if (strcmp("hello", tmp.str)) { - fprintf(stderr, "table_reader.read_entry_field (17) inconsistency\n"); + fprintf(stderr, "table_reader.read_entry_field (21) inconsistency\n"); exit(1); } // read and write from newly-created thread (dynamic field) if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) { - fprintf(stderr, "table_reader.read_entry_field (18) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.read_entry_field (22) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } if (tmp.u64 != 0) { - fprintf(stderr, "table_reader.read_entry_field (18) inconsistency\n"); + fprintf(stderr, "table_reader.read_entry_field (22) inconsistency\n"); exit(1); } tmp.u64 = 5; if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) { - fprintf(stderr, "table_reader.write_entry_field (19) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.write_entry_field (23) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) { - fprintf(stderr, "table_reader.read_entry_field (20) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.read_entry_field (24) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } if (tmp.u64 != 5) { - fprintf(stderr, "table_reader.read_entry_field (20) inconsistency\n"); + fprintf(stderr, "table_reader.read_entry_field (24) inconsistency\n"); exit(1); } @@ -336,7 +380,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp tmp.s64 = 10; if (SS_PLUGIN_SUCCESS == in->table_writer.erase_table_entry(ps->thread_table, &tmp)) { - fprintf(stderr, "table_reader.erase_table_entry (21) inconsistency\n"); + fprintf(stderr, "table_reader.erase_table_entry (25) inconsistency\n"); exit(1); } @@ -344,13 +388,13 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp tmp.s64 = 10000; if (SS_PLUGIN_SUCCESS != in->table_writer.erase_table_entry(ps->thread_table, &tmp)) { - fprintf(stderr, "table_reader.erase_table_entry (22) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.erase_table_entry (26) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } size = in->table_reader.get_table_size(ps->thread_table); if (size != 1) { - fprintf(stderr, "table_reader.get_table_size (23) failure: %s\n", in->get_owner_last_error(in->owner)); + fprintf(stderr, "table_reader.get_table_size (27) failure: %s\n", in->get_owner_last_error(in->owner)); exit(1); } From 919c8bd0a49ef20a4edf39d02a8872e46403bad6 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 20 Jul 2023 14:26:01 +0000 Subject: [PATCH 21/66] fix(upserspace/libsinsp): solve string allocation issues in plugin state access Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 49 ++++++++---- userspace/libsinsp/state/dynamic_struct.h | 94 ++++++++++++++++++----- userspace/libsinsp/state/type_info.h | 41 ---------- 3 files changed, 112 insertions(+), 72 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index 9d1583e27c..edd31d0534 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -311,12 +311,25 @@ struct plugin_table_wrapper: public libsinsp::state::table { throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "read field failure: " + m_owner->get_last_error()); } - #define _X(_type, _dtype) \ - { \ - convert_types(dout._dtype, *(_type*) out); \ + + // 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::PT_CHARBUF) + { + *(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 } - __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 @@ -324,12 +337,24 @@ struct plugin_table_wrapper: public libsinsp::state::table const auto& infos = get_plugin_field_infos(); ss_plugin_state_data v; - #define _X(_type, _dtype) \ - { \ - convert_types(*(_type*) in, v._dtype); \ + // 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::PT_CHARBUF) + { + 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 } - __PLUGIN_STATETYPE_SWITCH(typeinfo_to_state_type(i.info())); - #undef _X auto rc = m_input->writer.write_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &v); if (rc != SS_PLUGIN_SUCCESS) @@ -976,9 +1001,7 @@ ss_plugin_rc sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, ss_plu if (a->dynamic) \ { \ auto aa = static_cast*>(a->accessor); \ - _type _v; \ - e->get_dynamic_field(*aa, _v); \ - convert_types(_v, out->_dtype); \ + e->get_dynamic_field(*aa, out->_dtype); \ } \ else \ { \ diff --git a/userspace/libsinsp/state/dynamic_struct.h b/userspace/libsinsp/state/dynamic_struct.h index 195431545b..aa11d4e733 100644 --- a/userspace/libsinsp/state/dynamic_struct.h +++ b/userspace/libsinsp/state/dynamic_struct.h @@ -264,28 +264,20 @@ class dynamic_struct /** * @brief Accesses a field with the given accessor and reads its value. */ - template - inline void get_dynamic_field(const field_accessor& a, T& out) + template + inline void get_dynamic_field(const field_accessor& a, Val& out) { - if (!a.info().valid()) - { - throw sinsp_exception("can't get invalid field in dynamic struct"); - } - _check_defsptr(a.info().m_defsptr); + _check_defsptr(a.info(), false); get_dynamic_field(a.info(), reinterpret_cast(&out)); } /** * @brief Accesses a field with the given accessor and writes its value. */ - template - inline void set_dynamic_field(const field_accessor& a, const T& in) + template + inline void set_dynamic_field(const field_accessor& a, const Val& in) { - if (!a.info().valid()) - { - throw sinsp_exception("can't set invalid field in dynamic struct"); - } - _check_defsptr(a.info().m_defsptr); + _check_defsptr(a.info(), true); if (a.info().readonly()) { throw sinsp_exception("can't set a read-only dynamic struct field: " + a.info().name()); @@ -320,23 +312,59 @@ class dynamic_struct } protected: + /** + * @brief Gets the value of a dynamic field and writes it into "out". + * "out" points to a variable having the type of the field_info argument, + * according to the type definitions supported in libsinsp::state::typeinfo. + * For strings, "out" is considered of type const char**. + */ virtual void get_dynamic_field(const field_info& i, void* out) { - i.info().copy(_access_dynamic_field(i.m_index), out); + const auto* buf = _access_dynamic_field(i.m_index); + if (i.info().index() == PT_CHARBUF) + { + *((const char**) out) = ((const std::string*) buf)->c_str(); + } + else + { + memcpy(out, buf, i.info().size()); + } } + /** + * @brief Sets the value of a dynamic field by reading it from "in". + * "in" points to a variable having the type of the field_info argument, + * according to the type definitions supported in libsinsp::state::typeinfo. + * For strings, "in" is considered of type const char**. + */ virtual void set_dynamic_field(const field_info& i, const void* in) { - i.info().copy(in, _access_dynamic_field(i.m_index)); + auto* buf = _access_dynamic_field(i.m_index); + if (i.info().index() == PT_CHARBUF) + { + *((std::string*) buf) = *((const char**) in); + } + else + { + memcpy(buf, in, i.info().size()); + } } private: - inline void _check_defsptr(void* ptr) const + inline void _check_defsptr(const field_info& i, bool write) const { - if (m_dynamic_fields.get() != ptr) + if (!i.valid()) + { + throw sinsp_exception("can't set invalid field in dynamic struct"); + } + if (m_dynamic_fields.get() != i.m_defsptr) { throw sinsp_exception("using dynamic field accessor on struct it was not created from"); } + if (write && i.readonly()) + { + throw sinsp_exception("can't set a read-only dynamic struct field: " + i.name()); + } } inline void* _access_dynamic_field(size_t index) @@ -368,3 +396,33 @@ class dynamic_struct }; // state }; // libsinsp + +// specializations for string types + +template<> inline void libsinsp::state::dynamic_struct::get_dynamic_field( + const field_accessor& a, const char*& out) +{ + _check_defsptr(a.info(), false); + get_dynamic_field(a.info(), reinterpret_cast(&out)); +} + +template<> inline void libsinsp::state::dynamic_struct::get_dynamic_field( + const field_accessor& a, std::string& out) +{ + const char* s = NULL; + get_dynamic_field(a, s); + out = s; +} + +template <> inline void libsinsp::state::dynamic_struct::set_dynamic_field( + const field_accessor& a, const char* const& in) +{ + _check_defsptr(a.info(), true); + set_dynamic_field(a.info(), reinterpret_cast(&in)); +} + +template <> inline void libsinsp::state::dynamic_struct::set_dynamic_field( + const field_accessor& a, const std::string& in) +{ + set_dynamic_field(a, in.c_str()); +} diff --git a/userspace/libsinsp/state/type_info.h b/userspace/libsinsp/state/type_info.h index 2e3ac0bfdc..70c07e39ad 100644 --- a/userspace/libsinsp/state/type_info.h +++ b/userspace/libsinsp/state/type_info.h @@ -16,7 +16,6 @@ limitations under the License. */ #pragma once -#include "../sinsp_public.h" #include "../sinsp_exception.h" #include "../../driver/ppm_events_public.h" @@ -111,46 +110,6 @@ class typeinfo if (p && m_destroy) m_destroy(p); } - inline void copy(const void* from, void* to) const noexcept - { - switch(m_index) - { - case PT_INT8: - *((int8_t*) to) = *((const int8_t*) from); - break; - case PT_INT16: - *((int16_t*) to) = *((const int16_t*) from); - break; - case PT_INT32: - *((int32_t*) to) = *((const int32_t*) from); - break; - case PT_INT64: - *((int64_t*) to) = *((const int64_t*) from); - break; - case PT_UINT8: - *((uint8_t*) to) = *((const uint8_t*) from); - break; - case PT_UINT16: - *((uint16_t*) to) = *((const uint16_t*) from); - break; - case PT_UINT32: - *((uint32_t*) to) = *((const uint32_t*) from); - break; - case PT_UINT64: - *((uint64_t*) to) = *((const uint64_t*) from); - break; - case PT_CHARBUF: - *((std::string*) to) = *((const std::string*) from); - break; - case PT_BOOL: - *((bool*) to) = *((const bool*) from); - break; - default: - ASSERT(false); - break; - } - } - private: inline typeinfo(const char* n, index_t k, size_t s, void (*c)(void*), void (*d)(void*)) : m_name(n), m_index(k), m_size(s), m_construct(c), m_destroy(d) { } From 69f4eff58fb997083592185978449ef35b836f48 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 20 Jul 2023 15:30:12 +0000 Subject: [PATCH 22/66] update(userspace/libsinsp): static struct api parity, fixes, avoid mem copies Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 17 +++---------- userspace/libsinsp/state/dynamic_struct.h | 9 ++++++- userspace/libsinsp/state/static_struct.h | 22 ++++++++++++++--- userspace/libsinsp/test/state.ut.cpp | 30 ++++++++++++++++++++--- 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index edd31d0534..b15fc9f03c 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -93,17 +93,6 @@ template<> inline void convert_types(const std::string& from, const char*& to) to = from.c_str(); } -template<> inline void convert_types(const char* const& from, std::string& to) -{ - if (!from || *from == '\0') - { - to.clear(); - } - else - { - to = from; - } -} static inline std::string table_input_error_prefix(const sinsp_plugin* o, ss_plugin_table_input* i) { @@ -325,7 +314,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { #define _X(_type, _dtype) \ { \ - convert_types(dout._dtype, *(_type*) out); \ + *((_type*) out) = dout._dtype; \ } __PLUGIN_STATETYPE_SWITCH(typeinfo_to_state_type(i.info())); #undef _X @@ -350,7 +339,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { #define _X(_type, _dtype) \ { \ - convert_types(*(_type*) in, v._dtype); \ + convert_types(*((_type*) in), v._dtype); \ } __PLUGIN_STATETYPE_SWITCH(typeinfo_to_state_type(i.info())); #undef _X @@ -1006,7 +995,7 @@ ss_plugin_rc sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, ss_plu else \ { \ auto aa = static_cast*>(a->accessor); \ - convert_types(e->get_static_field(*aa), out->_dtype); \ + e->get_static_field(*aa, out->_dtype); \ } \ return SS_PLUGIN_SUCCESS; \ } diff --git a/userspace/libsinsp/state/dynamic_struct.h b/userspace/libsinsp/state/dynamic_struct.h index aa11d4e733..5ac445f59c 100644 --- a/userspace/libsinsp/state/dynamic_struct.h +++ b/userspace/libsinsp/state/dynamic_struct.h @@ -411,7 +411,14 @@ template<> inline void libsinsp::state::dynamic_struct::get_dynamic_field inline void libsinsp::state::dynamic_struct::set_dynamic_field( diff --git a/userspace/libsinsp/state/static_struct.h b/userspace/libsinsp/state/static_struct.h index 91dcc38428..e336e2a223 100644 --- a/userspace/libsinsp/state/static_struct.h +++ b/userspace/libsinsp/state/static_struct.h @@ -199,12 +199,21 @@ class static_struct return *(reinterpret_cast((void*) (((uintptr_t) this) + a.info().m_offset))); } + /** + * @brief Accesses a field with the given accessor and reads its value. + */ + template + inline void get_static_field(const field_accessor& a, Val& out) const + { + out = get_static_field(a); + } + /** * @brief Accesses a field with the given accessor and writes its value. * An exception is thrown if the field is read-only. */ - template - inline void set_static_field(const field_accessor& a, const T& v) + template + inline void set_static_field(const field_accessor& a, const Val& in) { if (!a.info().valid()) { @@ -214,7 +223,7 @@ class static_struct { throw sinsp_exception("can't set a read-only static struct field: " + a.info().name()); } - *(reinterpret_cast((void*) (((uintptr_t) this) + a.info().m_offset))) = v; + *(reinterpret_cast((void*) (((uintptr_t) this) + a.info().m_offset))) = in; } /** @@ -260,3 +269,10 @@ class static_struct }; // state }; // libsinsp + +// specializations for strings +template <> inline void libsinsp::state::static_struct::get_static_field( + const field_accessor& a, const char*& out) const +{ + out = get_static_field(a).c_str(); +} diff --git a/userspace/libsinsp/test/state.ut.cpp b/userspace/libsinsp/test/state.ut.cpp index 4506397644..477c7e465a 100644 --- a/userspace/libsinsp/test/state.ut.cpp +++ b/userspace/libsinsp/test/state.ut.cpp @@ -104,7 +104,9 @@ TEST(static_struct, defs_and_access) ASSERT_EQ(s.get_static_field(acc_num), 0); s.set_num(5); ASSERT_EQ(s.get_num(), 5); - ASSERT_EQ(s.get_static_field(acc_num), 5); + uint32_t u32tmp = 0; + s.get_static_field(acc_num, u32tmp); + ASSERT_EQ(u32tmp, 5); s.set_static_field(acc_num, (uint32_t) 6); ASSERT_EQ(s.get_num(), 6); ASSERT_EQ(s.get_static_field(acc_num), 6); @@ -115,8 +117,20 @@ TEST(static_struct, defs_and_access) str = "hello"; s.set_str("hello"); ASSERT_EQ(s.get_str(), str); - ASSERT_EQ(s.get_static_field(acc_str), str); - ASSERT_ANY_THROW(s.set_static_field(acc_str, str)); // readonly + s.get_static_field(acc_str, str); + ASSERT_EQ(str, "hello"); + ASSERT_ANY_THROW(s.set_static_field(acc_str, "hello")); // readonly + + const char* cstr = "sample"; + s.set_str(""); + s.get_static_field(acc_str, cstr); + ASSERT_EQ(strcmp(cstr, ""), 0); + s.set_str("hello"); + s.get_static_field(acc_str, cstr); + ASSERT_EQ(strcmp(cstr, "hello"), 0); + ASSERT_EQ(cstr, s.get_str().c_str()); + ASSERT_ANY_THROW(s.set_static_field(acc_str, cstr)); // readonly + // illegal access from an accessor created from different definition list // note: this should supposedly be checked for and throw an exception, @@ -187,6 +201,16 @@ TEST(dynamic_struct, defs_and_access) s.set_dynamic_field(acc_str, std::string("hello")); s.get_dynamic_field(acc_str, tmpstr); ASSERT_EQ(tmpstr, std::string("hello")); + + s.set_dynamic_field(acc_str, std::string("")); + const char* ctmpstr = "sample"; + s.get_dynamic_field(acc_str, ctmpstr); + ASSERT_EQ(strcmp(ctmpstr, ""), 0); + ctmpstr = "hello"; + s.set_dynamic_field(acc_str, ctmpstr); + ctmpstr = ""; + s.get_dynamic_field(acc_str, ctmpstr); + ASSERT_EQ(strcmp(ctmpstr, "hello"), 0); // illegal access from an accessor created from different definition list auto fields2 = std::make_shared(); From 77e3c5731b02bc526967a6c1a48e24a3669d3245 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Thu, 20 Jul 2023 17:52:26 +0000 Subject: [PATCH 23/66] fix(userspace/libsinsp): solve address sanitizer issues Signed-off-by: Jason Dellaluce --- .../libsinsp/test/plugins/sample_table.h | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/userspace/libsinsp/test/plugins/sample_table.h b/userspace/libsinsp/test/plugins/sample_table.h index df19c8edd9..ecb73145f4 100644 --- a/userspace/libsinsp/test/plugins/sample_table.h +++ b/userspace/libsinsp/test/plugins/sample_table.h @@ -93,12 +93,33 @@ class sample_table 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); - t->strings.push_back(name); + for (size_t i = 0; i < t->fields.size(); i++) + { + const auto& f = t->fields[i]; + if (strcmp(f.name, name) == 0) + { + if (f.field_type != data_type) + { + t->lasterr = "field defined with incompatible types: " + std::string(name); + return NULL; + } + // note: shifted by 1 so that we never return 0 (interpreted as NULL) + return (ss_plugin_table_field_t*) (i + 1); + } + } + ss_plugin_table_fieldinfo f; - f.name = t->strings[t->strings.size() - 1].c_str(); + t->strings.push_back(name); f.field_type = data_type; f.read_only = false; t->fields.push_back(f); + for (size_t i = 0; i < t->fields.size(); i++) + { + // note: previous string pointers may have been changed so we + // we need to set all of them again + t->fields[i].name = t->strings[i].c_str(); + } + // note: shifted by 1 so that we never return 0 (interpreted as NULL) return (ss_plugin_table_field_t*) (t->fields.size()); } From 49e9be4ccf62d6473411c826e4fd5e852c081e22 Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Thu, 20 Jul 2023 18:38:17 +0200 Subject: [PATCH 24/66] new: return ENOTSUP when the modern probe is not supported Signed-off-by: Andrea Terzolo --- userspace/libscap/engine/modern_bpf/scap_modern_bpf.c | 4 ++-- userspace/libscap/examples/01-open/scap_open.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c b/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c index 0dd83f124f..797d867ec0 100644 --- a/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c +++ b/userspace/libscap/engine/modern_bpf/scap_modern_bpf.c @@ -167,12 +167,12 @@ int32_t scap_modern_bpf__init(scap_t* handle, scap_open_args* oargs) */ if(check_buffer_bytes_dim(handle->m_lasterr, params->buffer_bytes_dim) != SCAP_SUCCESS) { - return SCAP_FAILURE; + return ENOTSUP; } if(!pman_check_support()) { - return SCAP_FAILURE; + return ENOTSUP; } /* Initialize the libpman internal state. diff --git a/userspace/libscap/examples/01-open/scap_open.c b/userspace/libscap/examples/01-open/scap_open.c index 8fa8c66199..d9d65728f6 100644 --- a/userspace/libscap/examples/01-open/scap_open.c +++ b/userspace/libscap/examples/01-open/scap_open.c @@ -856,7 +856,7 @@ int main(int argc, char** argv) if(g_h == NULL || res != SCAP_SUCCESS) { fprintf(stderr, "%s (%d)\n", error, res); - return EXIT_FAILURE; + return res; } print_start_capture(); From d9891460eb75bfda7d6862ec0fb78e01872250fa Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 08:58:25 +0200 Subject: [PATCH 25/66] new(ci, docs): added kernel tests CI jobs that build up a kernel test matrix as gh-pages. Signed-off-by: Federico Di Pierro --- .github/workflows/kernel_tests.yaml | 77 +++++++++++++++++++++++++++++ .github/workflows/pages.yml | 53 ++++++++++++++++++++ docs/index.md | 30 +++++++++++ mkdocs.yml | 8 +++ 4 files changed, 168 insertions(+) create mode 100644 .github/workflows/kernel_tests.yaml create mode 100644 .github/workflows/pages.yml create mode 100644 docs/index.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml new file mode 100644 index 0000000000..194b702cd0 --- /dev/null +++ b/.github/workflows/kernel_tests.yaml @@ -0,0 +1,77 @@ +name: Test drivers against a matrix of kernels/distros + +on: + workflow_dispatch: + inputs: + libsversion: + description: libs version to be tested + type: string + required: false + default: master + push: + branches: + - master + +concurrency: + group: kernel-tests-master + cancel-in-progress: true + +jobs: + test-kernels: + strategy: + fail-fast: false + matrix: + architecture: [X64, ARM64] + runs-on: [ "self-hosted", "linux", "${{matrix.architecture}}" ] + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + repository: alacuku/e2e-falco-tests + + - name: Generate vars yaml + run: | + LIBS_V=${{ github.event.inputs.libsversion }} + LIBS_VERSION=${LIBS_V:-"master"} + cat > vars.yml < means that the test was successful +* 🟡 -> means that the test was skipped; you can click the symbol to reach the test section and checkout why the test was skipped. +* ❌ -> means that the test failed; you can click the symbol to reach the test section and checkout why the test failed. + + diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000000..98ec47ab25 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,8 @@ +site_name: Falco Kernel Testing +site_url: https://github.com/alacuku/e2e-falco-tests/ +nav: + - Home: index.md + - AMD64: matrix_X64.md + - ARM64: matrix_ARM64.md + +theme: material From 9cfa6a7e6aa93703ec60e979c17d2a20346400eb Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 09:53:46 +0200 Subject: [PATCH 26/66] chore(docs): added readme badge. Signed-off-by: Federico Di Pierro --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6e49031ac5..95feb9c096 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![CI Build](https://github.com/falcosecurity/libs/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/falcosecurity/libs/actions/workflows/ci.yml) [![Architectures](https://img.shields.io/badge/ARCHS-x86__64%7Caarch64%7Cs390x-blueviolet)](#drivers-officially-supported-architectures) [![Drivers](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/FedeDP/1cbc5d42edf8e3a02fb75e76625f1072/raw/kernel.json)](https://github.com/falcosecurity/libs/actions/workflows/latest-kernel.yml) +[![Kernel Tests](https://github.com/falcosecuritylibs/actions/workflows/kernel_tests.yaml/badge.svg)](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml) As per the [OSS Libraries Contribution Plan](https://github.com/falcosecurity/falco/blob/master/proposals/20210119-libraries-contribution.md), this repository has been chosen to be the new home for **libsinsp**, **libscap**, the **kernel module** and the **eBPF probe** sources. Refer to https://falco.org/blog/contribution-drivers-kmod-ebpf-libraries/ for more information. From d64d2d14723ae52e209fedca7ef8613e1445fbb3 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 10:44:41 +0200 Subject: [PATCH 27/66] chore(driver,docs): moved driver/report.md under gh pages. Signed-off-by: Federico Di Pierro --- docs/index.md | 17 +++++++++++++---- {driver => docs}/report.md | 7 ------- mkdocs.yml | 6 ++++-- 3 files changed, 17 insertions(+), 13 deletions(-) rename {driver => docs}/report.md (97%) diff --git a/docs/index.md b/docs/index.md index 0725407e7d..4ab1426743 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,8 @@ -# Home of Falco kernel testing support matrixes +# Falcosecurity drivers -Here you can find kernel testing support matrixes for the [Falco](https://falco.org/) project. +## Home of Falco drivers kernel testing matrixes + +Here you can find kernel testing support matrixes for [Falco](https://falco.org/) drivers. For more info, make sure to read the [driver kernel testing framework proposal](https://github.com/falcosecurity/libs/blob/master/proposals/20230530-driver-kernel-testing-framework.md). Basically, we use Ansible playbooks to spawn Firecracker microvms where we can test: @@ -14,17 +16,24 @@ The modern-bpf driver-enabled scap-open is built using the exactly same process * scap-open with embedded modern-bpf skeleton is built on a centos7 machine to allow largest possible support (old glibc version) * scap-open binary is copied to each spawned vm -## Supported Archs +### Supported Archs For now, supported architectures are: * AMD64 * ARM64 -## Glossary +### Glossary * 🟢 -> means that the test was successful * 🟡 -> means that the test was skipped; you can click the symbol to reach the test section and checkout why the test was skipped. * ❌ -> means that the test failed; you can click the symbol to reach the test section and checkout why the test failed. +## Syscalls Report + +You can also find the list of supported syscalls by our drivers, be it through specific filler or generic. + +### Glossary +* 🟢 -> means that the syscall is implemented as a specific event +* 🟡 -> means that the syscall is implemented as a generic event diff --git a/driver/report.md b/docs/report.md similarity index 97% rename from driver/report.md rename to docs/report.md index af62a98dad..ecd941fbac 100644 --- a/driver/report.md +++ b/docs/report.md @@ -1,10 +1,3 @@ -# Supported Syscalls - -This table represents the syscalls supported by our drivers. - -🟢 means that the syscall is fully instrumented so its parameters are available to userspace. -🟡 means that the syscall is not fully instrumented so the userspace is just notified when the syscall happens but no parameters are available. - | SYSCALL | SUPPORTED | |-------------------------|-----------| | _sysctl | 🟡 | diff --git a/mkdocs.yml b/mkdocs.yml index 98ec47ab25..98aa68e7f5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,7 +2,9 @@ site_name: Falco Kernel Testing site_url: https://github.com/alacuku/e2e-falco-tests/ nav: - Home: index.md - - AMD64: matrix_X64.md - - ARM64: matrix_ARM64.md + - Matrixes: + - amd64: matrix_X64.md + - arm64: matrix_ARM64.md + - Syscalls Report: report.md theme: material From c4d1e4785d15b319142a5688f1073ae2781e0668 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 10:50:52 +0200 Subject: [PATCH 28/66] chore(docs): added 2 new home pages for syscalls report and matrixes. Signed-off-by: Federico Di Pierro --- docs/index.md | 31 +------------------------------ docs/matrix.md | 25 +++++++++++++++++++++++++ docs/syscalls.md | 10 ++++++++++ mkdocs.yml | 5 ++++- 4 files changed, 40 insertions(+), 31 deletions(-) create mode 100644 docs/matrix.md create mode 100644 docs/syscalls.md diff --git a/docs/index.md b/docs/index.md index 4ab1426743..a04eb87b6e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,39 +1,10 @@ # Falcosecurity drivers -## Home of Falco drivers kernel testing matrixes +## Falco drivers kernel testing matrixes Here you can find kernel testing support matrixes for [Falco](https://falco.org/) drivers. For more info, make sure to read the [driver kernel testing framework proposal](https://github.com/falcosecurity/libs/blob/master/proposals/20230530-driver-kernel-testing-framework.md). -Basically, we use Ansible playbooks to spawn Firecracker microvms where we can test: - -* kmod and ebpf drivers build -* scap-open run with {kmod,ebpf,modern-bpf} - -The modern-bpf driver-enabled scap-open is built using the exactly same process used by [Falco release pipeline](https://github.com/falcosecurity/falco/blob/master/.github/workflows/reusable_build_packages.yaml#L15): - -* the modern bpf skeleton is built on a Fedora machine -* scap-open with embedded modern-bpf skeleton is built on a centos7 machine to allow largest possible support (old glibc version) -* scap-open binary is copied to each spawned vm - -### Supported Archs - -For now, supported architectures are: - -* AMD64 -* ARM64 - -### Glossary - -* 🟢 -> means that the test was successful -* 🟡 -> means that the test was skipped; you can click the symbol to reach the test section and checkout why the test was skipped. -* ❌ -> means that the test failed; you can click the symbol to reach the test section and checkout why the test failed. - ## Syscalls Report You can also find the list of supported syscalls by our drivers, be it through specific filler or generic. - -### Glossary - -* 🟢 -> means that the syscall is implemented as a specific event -* 🟡 -> means that the syscall is implemented as a generic event diff --git a/docs/matrix.md b/docs/matrix.md new file mode 100644 index 0000000000..348d0c36b9 --- /dev/null +++ b/docs/matrix.md @@ -0,0 +1,25 @@ +# Home of Falco drivers kernel testing matrixes + +Basically, we use Ansible playbooks to spawn Firecracker microvms where we can test: + +* kmod and ebpf drivers build +* scap-open run with {kmod,ebpf,modern-bpf} + +The modern-bpf driver-enabled scap-open is built using the exactly same process used by [Falco release pipeline](https://github.com/falcosecurity/falco/blob/master/.github/workflows/reusable_build_packages.yaml#L15): + +* the modern bpf skeleton is built on a Fedora machine +* scap-open with embedded modern-bpf skeleton is built on a centos7 machine to allow largest possible support (old glibc version) +* scap-open binary is copied to each spawned vm + +## Supported Archs + +For now, supported architectures are: + +* AMD64 +* ARM64 + +## Glossary + +* 🟢 -> means that the test was successful +* 🟡 -> means that the test was skipped; you can click the symbol to reach the test section and checkout why the test was skipped. +* ❌ -> means that the test failed; you can click the symbol to reach the test section and checkout why the test failed. diff --git a/docs/syscalls.md b/docs/syscalls.md new file mode 100644 index 0000000000..206dbf6de0 --- /dev/null +++ b/docs/syscalls.md @@ -0,0 +1,10 @@ +# Home of Falco drivers syscalls report + +Thanks to our [syscalls-bumper](https://github.com/falcosecurity/syscalls-bumper) project, we are able to always support latest syscalls added to linux kernel. +Support for new syscalls is initially automatically added by the tool as generic events; when needed, a generic event can be made "specific", +by creating a whole new event to track it. + +## Glossary + +* 🟢 -> means that the syscall is implemented as a specific event +* 🟡 -> means that the syscall is implemented as a generic event diff --git a/mkdocs.yml b/mkdocs.yml index 98aa68e7f5..5cb5d91cd0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,8 +3,11 @@ site_url: https://github.com/alacuku/e2e-falco-tests/ nav: - Home: index.md - Matrixes: + - matrix.md - amd64: matrix_X64.md - arm64: matrix_ARM64.md - - Syscalls Report: report.md + - Syscalls: + - syscalls.md + - Report: report.md theme: material From 52dd29afad6e829c869f5c023c74db1068ffbc6c Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 10:52:21 +0200 Subject: [PATCH 29/66] chore(docs): updated path to report.md in readme. Signed-off-by: Federico Di Pierro --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 95feb9c096..c8d3ac6575 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Right now our drivers officially support the following architectures: | **aarch64** | >= [3.16](https://github.com/torvalds/linux/commit/055b1212d141f1f398fca548f8147787c0b6253f) | >= 4.17 | >= 5.8 | _STABLE_ | | **s390x** | >= 2.6 | >= [5.5](https://github.com/torvalds/linux/commit/6ae08ae3dea) | >= 5.8 | _EXPERIMENTAL_ | -**For a list of supported syscalls through specific events, please refer to [_report_](./driver/report.md).** +**For a list of supported syscalls through specific events, please refer to [_report_](./docs/report.md).** > **NOTE:** while we strive to achieve maximum compatibility, we cannot assure that drivers correctly build against a new kernel version minutes after it gets released, since we might need to make some adjustments. > To get properly notified whenever drivers stop building, we have a [CI workflow](.github/workflows/latest-kernel.yml) that tests the build against the [latest mainline kernel](https://www.kernel.org/) (RC too!) From 1e8a03289815db0399d421b9938237026a926db4 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 11:33:08 +0200 Subject: [PATCH 30/66] chore: added requirements.txt file for github pages deploy. Signed-off-by: Federico Di Pierro --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..9a8a4ca477 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +mkdocs +mkdocs-material From f4112adac4a0018ccfbc6edcecf5c9b93f2c6c59 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 12:51:32 +0200 Subject: [PATCH 31/66] chore(docs): update gh pages urls and title. Signed-off-by: Federico Di Pierro --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 5cb5d91cd0..35b1e76561 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ -site_name: Falco Kernel Testing -site_url: https://github.com/alacuku/e2e-falco-tests/ +site_name: Falco Drivers +site_url: https://github.com/falcosecurity/libs nav: - Home: index.md - Matrixes: From 84b3fad854ed1c3a3c54855e96b2ecb511ef4e6c Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 12:59:43 +0200 Subject: [PATCH 32/66] chore(docs): added gh pages badge. Signed-off-by: Federico Di Pierro --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c8d3ac6575..61deae68c9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Architectures](https://img.shields.io/badge/ARCHS-x86__64%7Caarch64%7Cs390x-blueviolet)](#drivers-officially-supported-architectures) [![Drivers](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/FedeDP/1cbc5d42edf8e3a02fb75e76625f1072/raw/kernel.json)](https://github.com/falcosecurity/libs/actions/workflows/latest-kernel.yml) [![Kernel Tests](https://github.com/falcosecuritylibs/actions/workflows/kernel_tests.yaml/badge.svg)](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml) +[![Github Pages](https://github.com/falcosecurity/libs/actions/workflows/pages.yml/badge.svg)](https://falcosecurity.github.io/libs/) As per the [OSS Libraries Contribution Plan](https://github.com/falcosecurity/falco/blob/master/proposals/20210119-libraries-contribution.md), this repository has been chosen to be the new home for **libsinsp**, **libscap**, the **kernel module** and the **eBPF probe** sources. Refer to https://falco.org/blog/contribution-drivers-kmod-ebpf-libraries/ for more information. From 69d66239c83394b1f74698309ac0060ab753ccd1 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 13:08:39 +0200 Subject: [PATCH 33/66] chore: renamed gh pages site. Signed-off-by: Federico Di Pierro --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 35b1e76561..4d6a11e691 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: Falco Drivers +site_name: Falcosecurity Drivers site_url: https://github.com/falcosecurity/libs nav: - Home: index.md From 7cc38e9c0b1f8d990eff0ec029147787eb48aa3c Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Mon, 24 Jul 2023 13:57:27 +0200 Subject: [PATCH 34/66] fix(docs): fixed readme link to kenrel_tests workflow. Signed-off-by: Federico Di Pierro --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61deae68c9..bd2c5286b0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![CI Build](https://github.com/falcosecurity/libs/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/falcosecurity/libs/actions/workflows/ci.yml) [![Architectures](https://img.shields.io/badge/ARCHS-x86__64%7Caarch64%7Cs390x-blueviolet)](#drivers-officially-supported-architectures) [![Drivers](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/FedeDP/1cbc5d42edf8e3a02fb75e76625f1072/raw/kernel.json)](https://github.com/falcosecurity/libs/actions/workflows/latest-kernel.yml) -[![Kernel Tests](https://github.com/falcosecuritylibs/actions/workflows/kernel_tests.yaml/badge.svg)](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml) +[![Kernel Tests](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml/badge.svg)](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml) [![Github Pages](https://github.com/falcosecurity/libs/actions/workflows/pages.yml/badge.svg)](https://falcosecurity.github.io/libs/) As per the [OSS Libraries Contribution Plan](https://github.com/falcosecurity/falco/blob/master/proposals/20210119-libraries-contribution.md), this repository has been chosen to be the new home for **libsinsp**, **libscap**, the **kernel module** and the **eBPF probe** sources. From b8286a3b3ddb0e4be0f8f9d2b70fb59bef15ac5b Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Tue, 25 Jul 2023 09:38:36 +0200 Subject: [PATCH 35/66] update(ci/kernel-tests): run kernel tests step by step Instead of running all the playbooks through the main playbook, we run each playbook on its own. The outcome is the same, but it's easier to debug and check the CI logs. Signed-off-by: Aldo Lacuku --- .github/workflows/kernel_tests.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index 194b702cd0..c41b7e4018 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -40,9 +40,21 @@ jobs: libs: {name: "falcosecurity-libs", repo: "https://github.com/falcosecurity/libs.git", version: "$LIBS_VERSION"} EOF - - name: Run kernels tests + - name: Bootstrap VMs run: | - ansible-playbook main-playbook.yml --extra-vars "@vars.yml" || : + ansible-playbook bootstrap.yml --extra-vars "@vars.yml" + + - name: Common setup + run: | + ansible-playbook common.yml --extra-vars "@vars.yml" + + - name: Prepare github repos + run: | + ansible-playbook git-repos.yml --extra-vars "@vars.yml" + + - name: Run scap-open tests + run: | + ansible-playbook scap-open.yml --extra-vars "@vars.yml" || : - name: Tar output files run: | From 828b47e1dbdf23d0937df7692895d940a1ec699d Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 07:54:39 +0000 Subject: [PATCH 36/66] update(userspace/plugin): bump plugin API to v3.1.0 Signed-off-by: Jason Dellaluce --- userspace/plugin/plugin_api.h | 117 ++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 13 deletions(-) diff --git a/userspace/plugin/plugin_api.h b/userspace/plugin/plugin_api.h index 14bfcff8e2..2f076a7dbd 100644 --- a/userspace/plugin/plugin_api.h +++ b/userspace/plugin/plugin_api.h @@ -27,8 +27,10 @@ extern "C" { // // API versions of this plugin framework // +// todo(jasondellaluce): when/if major changes to v4, solve all todos +// in this file related to deprecated types. #define PLUGIN_API_VERSION_MAJOR 3 -#define PLUGIN_API_VERSION_MINOR 0 +#define PLUGIN_API_VERSION_MINOR 1 #define PLUGIN_API_VERSION_PATCH 0 // @@ -44,6 +46,16 @@ extern "C" { // #define PLUGIN_MAX_ERRLEN 1024 +// Supported by the API but deprecated. Use the extended version ss_plugin_table_reader_vtable_ext instead. +// todo(jasondellaluce): when/if major changes to v4, remove this and +// give this name to the associated *_ext struct. +typedef struct +{ + ss_plugin_table_fieldinfo* (*list_table_fields)(ss_plugin_table_t* t, uint32_t* nfields); + ss_plugin_table_field_t* (*get_table_field)(ss_plugin_table_t* t, const char* name, ss_plugin_state_type data_type); + ss_plugin_table_field_t* (*add_table_field)(ss_plugin_table_t* t, const char* name, ss_plugin_state_type data_type); +} ss_plugin_table_fields_vtable; + // Vtable for controlling and the fields for the entries of a state table. // This allows discovering the fields available in the table, defining new ones, // and obtaining accessors usable at runtime for reading and writing the fields' @@ -72,10 +84,29 @@ typedef struct // Returns NULL in case of issues (including when a field is defined multiple // times with different data types). ss_plugin_table_field_t* (*add_table_field)(ss_plugin_table_t* t, const char* name, ss_plugin_state_type data_type); -} ss_plugin_table_fields_vtable; +} ss_plugin_table_fields_vtable_ext; + +// Supported by the API but deprecated. Use the extended version ss_plugin_table_reader_vtable_ext instead. +// todo(jasondellaluce): when/if major changes to v4, remove this and +// give this name to the associated *_ext struct. +typedef struct +{ + const char* (*get_table_name)(ss_plugin_table_t* t); + uint64_t (*get_table_size)(ss_plugin_table_t* t); + ss_plugin_table_entry_t* (*get_table_entry)(ss_plugin_table_t* t, const ss_plugin_state_data* key); + 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); +} ss_plugin_table_reader_vtable; + +// Opaque pointer to the state data relative to a state table iteration. +// This is passed initially by the invoker when starting the iteration, and +// is then dispatched to the iterator for each of the entries of the table. +typedef void ss_plugin_table_iterator_state_t; + +// Iterator function callback used by a plugin for looping through all the +// entries of a given state table. Returns true if the iteration should +// proceed to the next element, or false in case of break out. +typedef ss_plugin_bool (*ss_plugin_table_iterator_func_t)(ss_plugin_table_iterator_state_t* s, ss_plugin_table_entry_t* e); -// Vtable for controlling a state table for read operations. -// todo(jasondellaluce): support looping over a table typedef struct { // Returns the table's name, or NULL in case of error. @@ -89,6 +120,8 @@ typedef struct // Returns an opaque pointer to an entry present in the table at the given // key, or NULL in case of issues (including if no entry is found at the // given key). The returned pointer is owned by the table's owner. + // Every non-NULL returned entry must be released by invoking release_table_entry() + // once it becomes no more used by the invoker. ss_plugin_table_entry_t* (*get_table_entry)(ss_plugin_table_t* t, const ss_plugin_state_data* key); // // Reads the value of an entry field from a table's entry. @@ -96,7 +129,30 @@ typedef struct // The read value is stored in the "out" parameter. // Returns SS_PLUGIN_SUCCESS if successful, and SS_PLUGIN_FAILURE otherwise. 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); -} ss_plugin_table_reader_vtable; + // + // Releases a table entry obtained by from previous invocation of get_table_entry(). + // After being released, the same table entry cannot be reused by the invoker. + // However, the same entry can be re-obtained through an invocation of get_table_entry(). + void (*release_table_entry)(ss_plugin_table_t* t, ss_plugin_table_entry_t* e); + // + // Iterates through all the entries of a table, invoking the interation + // callback function for each of them. Returns false in case of failure or + // iteration break-out, and true otherwise. + ss_plugin_bool (*iterate_entries)(ss_plugin_table_t* t, ss_plugin_table_iterator_func_t it, ss_plugin_table_iterator_state_t* s); +} ss_plugin_table_reader_vtable_ext; + +// Supported by the API but deprecated. Use the extended version ss_plugin_table_writer_vtable_ext instead. +// todo(jasondellaluce): when/if major changes to v4, remove this and +// give this name to the associated *_ext struct. +typedef struct +{ + ss_plugin_rc (*clear_table)(ss_plugin_table_t* t); + ss_plugin_rc (*erase_table_entry)(ss_plugin_table_t* t, const ss_plugin_state_data* key); + ss_plugin_table_entry_t* (*create_table_entry)(ss_plugin_table_t* t); + void (*destroy_table_entry)(ss_plugin_table_t* t, ss_plugin_table_entry_t* e); + ss_plugin_table_entry_t* (*add_table_entry)(ss_plugin_table_t* t, const ss_plugin_state_data* key, ss_plugin_table_entry_t* entry); + 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); +} ss_plugin_table_writer_vtable; // Vtable for controlling a state table for write operations. typedef struct @@ -133,7 +189,7 @@ typedef struct // The written value is read from the "in" parameter. // Returns SS_PLUGIN_SUCCESS if successful, and SS_PLUGIN_FAILURE otherwise. 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); -} ss_plugin_table_writer_vtable; +} ss_plugin_table_writer_vtable_ext; // Plugin-provided input passed to the add_table() callback of // ss_plugin_init_tables_input, that can be used by the plugin to inform its @@ -154,14 +210,29 @@ typedef struct // This will be passed as parameters to all the callbacks defined below. ss_plugin_table_t* table; // - // Vtable for controlling read operations on the state table. + // Supported but deprecated. Use the extended version reader_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. ss_plugin_table_reader_vtable reader; // - // Vtable for controlling write operations on the state table. + // Supported but deprecated. Use the extended version writer_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. ss_plugin_table_writer_vtable writer; // - // Vtable for controlling operations related to fields on the state table. + // Supported but deprecated. Use the extended version fields_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. ss_plugin_table_fields_vtable fields; + // + // Vtable for controlling read operations on the state table. + ss_plugin_table_reader_vtable_ext* reader_ext; + // + // Vtable for controlling write operations on the state table. + ss_plugin_table_writer_vtable_ext* writer_ext; + // + // Vtable for controlling operations related to fields on the state table. + ss_plugin_table_fields_vtable_ext* fields_ext; } ss_plugin_table_input; // Initialization-time input related to the event parsing capability. @@ -185,9 +256,14 @@ typedef struct // by other actors of the plugin's owner to interact with the state table. ss_plugin_rc (*add_table)(ss_plugin_owner_t* o, const ss_plugin_table_input* in); // + // Supported but deprecated. Use the extended version fields_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. + ss_plugin_table_fields_vtable fields; + // // Vtable for controlling operations related to fields on the state tables // registeted in the plugin's owner. - ss_plugin_table_fields_vtable fields; + ss_plugin_table_fields_vtable_ext* fields_ext; } ss_plugin_init_tables_input; // Input passed at the plugin through plugin_init(). This contain information @@ -240,8 +316,13 @@ typedef struct ss_plugin_field_extract_input // extract_fields() call. ss_plugin_extract_field *fields; // - // Vtable for controlling a state table for read operations. + // Supported but deprecated. Use the extended version table_reader_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. ss_plugin_table_reader_vtable table_reader; + // + // Vtable for controlling a state table for read operations. + ss_plugin_table_reader_vtable_ext* table_reader_ext; } ss_plugin_field_extract_input; // Input passed to the plugin when parsing an event for the event parsing @@ -258,11 +339,21 @@ typedef struct ss_plugin_event_parse_input // The string pointer is owned by the plugin's owenr. const char *(*get_owner_last_error)(ss_plugin_owner_t *o); // - // Vtable for controlling a state table for read operations. + // Supported but deprecated. Use the extended version table_reader_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. ss_plugin_table_reader_vtable table_reader; // - // Vtable for controlling a state table for write operations. + // Supported but deprecated. Use the extended version table_writer_ext. + // todo(jasondellaluce): when/if major changes to v4, remove this and + // give this name to the associated *_ext pointer. ss_plugin_table_writer_vtable table_writer; + // + // Vtable for controlling a state table for read operations. + ss_plugin_table_reader_vtable_ext* table_reader_ext; + // + // Vtable for controlling a state table for write operations. + ss_plugin_table_writer_vtable_ext* table_writer_ext; } ss_plugin_event_parse_input; // From 0994a448bedc269b0fe40fb702c8e73a45f4c352 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 14:14:20 +0000 Subject: [PATCH 37/66] fix(userspace/libsinsp): fill req api version in plugin class Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/userspace/libsinsp/plugin.cpp b/userspace/libsinsp/plugin.cpp index 02f9b0d49c..3e624e05ce 100755 --- a/userspace/libsinsp/plugin.cpp +++ b/userspace/libsinsp/plugin.cpp @@ -401,6 +401,13 @@ bool sinsp_plugin::resolve_dylib_symbols(std::string &errstr) errstr = "plugin provided an invalid version string: '" + version_str + "'"; return false; } + std::string req_api_version_str = str_from_alloc_charbuf(m_handle->api.get_required_api_version()); + m_required_api_version = sinsp_version(req_api_version_str); + if(!m_required_api_version.m_valid) + { + errstr = "plugin provided an invalid required api version string: '" + req_api_version_str + "'"; + return false; + } // read capabilities and process their info m_caps = plugin_get_capabilities(m_handle, err); From 70885fb481a54f158aafc5c9277be5e4c4d93f85 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 14:17:06 +0000 Subject: [PATCH 38/66] update(userspace/libsinsp): support plugin api v3.1.0 Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin.cpp | 16 +- userspace/libsinsp/plugin.h | 10 +- userspace/libsinsp/plugin_table_api.cpp | 335 ++++++++++++++++++------ 3 files changed, 278 insertions(+), 83 deletions(-) diff --git a/userspace/libsinsp/plugin.cpp b/userspace/libsinsp/plugin.cpp index 3e624e05ce..0e522a21c3 100755 --- a/userspace/libsinsp/plugin.cpp +++ b/userspace/libsinsp/plugin.cpp @@ -162,13 +162,15 @@ bool sinsp_plugin::init(const std::string &config, std::string &errstr) ss_plugin_init_input in; ss_plugin_init_tables_input tables_in; + ss_plugin_table_fields_vtable_ext table_fields_ext; in.owner = this; in.get_owner_last_error = sinsp_plugin::get_owner_last_error; in.tables = NULL; in.config = conf.c_str(); if (m_caps & (CAP_PARSING | CAP_EXTRACTION)) { - sinsp_plugin::table_field_api(tables_in.fields); + tables_in.fields_ext = &table_fields_ext; + sinsp_plugin::table_field_api(tables_in.fields, table_fields_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; @@ -810,11 +812,13 @@ bool sinsp_plugin::extract_fields(sinsp_evt* evt, uint32_t num_fields, ss_plugin ev.evtsrc = evt->get_source_name(); ss_plugin_field_extract_input in; + ss_plugin_table_reader_vtable_ext table_reader_ext; in.num_fields = num_fields; in.fields = fields; in.owner = (ss_plugin_owner_t *) this; in.get_owner_last_error = sinsp_plugin::get_owner_last_error; - sinsp_plugin::table_read_api(in.table_reader); + 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; } @@ -835,10 +839,14 @@ bool sinsp_plugin::parse_event(sinsp_evt* evt) const ev.evtsrc = evt->get_source_name(); ss_plugin_event_parse_input in; + ss_plugin_table_reader_vtable_ext table_reader_ext; + ss_plugin_table_writer_vtable_ext table_writer_ext; in.owner = (ss_plugin_owner_t *) this; in.get_owner_last_error = sinsp_plugin::get_owner_last_error; - sinsp_plugin::table_read_api(in.table_reader); - sinsp_plugin::table_write_api(in.table_writer); + in.table_reader_ext = &table_reader_ext; + 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); return res == SS_PLUGIN_SUCCESS; diff --git a/userspace/libsinsp/plugin.h b/userspace/libsinsp/plugin.h index 555701580a..0d2c901ba5 100755 --- a/userspace/libsinsp/plugin.h +++ b/userspace/libsinsp/plugin.h @@ -254,9 +254,9 @@ class sinsp_plugin libsinsp::events::set m_extract_event_codes; /** Event Parsing **/ - struct table_input_deleter { void operator()(ss_plugin_table_input* r); }; + 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; + 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; @@ -285,9 +285,9 @@ class sinsp_plugin static const char* get_owner_last_error(ss_plugin_owner_t* o); /** Event parsing helpers **/ - static void table_field_api(ss_plugin_table_fields_vtable& out); - static void table_read_api(ss_plugin_table_reader_vtable& out); - static void table_write_api(ss_plugin_table_writer_vtable& out); + 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); diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index b15fc9f03c..d6bf585b10 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -93,6 +93,133 @@ template<> inline void convert_types(const std::string& from, const char*& to) to = from.c_str(); } +static void noop_release_table_entry(ss_plugin_table_t*, ss_plugin_table_entry_t*) +{ +} + +static ss_plugin_bool noop_iterate_entries(ss_plugin_table_t*, ss_plugin_table_iterator_func_t, ss_plugin_table_iterator_state_t*) +{ + return 0; +} + +struct owned_table_input_deleter +{ + void operator()(ss_plugin_table_input* in) + { + delete in->reader_ext; + delete in->writer_ext; + delete in->fields_ext; + delete in; + } +}; + +using owned_table_input_t = std::shared_ptr; + +// note(jasondellaluce): here we assume that the api version has major number v3 +// todo(jasondellaluce): update the repairing logic and safety checks +// when switching to a v4 minor/major plugin API version +static inline owned_table_input_t copy_and_check_table_input(const sinsp_plugin* p, const ss_plugin_table_input* in) +{ + std::string errprefix = "failure in adding state table defined by plugin '" + p->name() + "': "; + if (!in) + { + throw sinsp_exception(errprefix + "input is null"); + } + if (!in->name) + { + throw sinsp_exception(errprefix + "name is null"); + } + + owned_table_input_t res( + new ss_plugin_table_input(), owned_table_input_deleter()); + res->name = in->name; + res->key_type = in->key_type; + res->table = in->table; + res->reader = in->reader; + res->writer = in->writer; + res->fields = in->fields; + + // note: before minor v1, we didn't have the "extended" vtables for state tables, + // so we need to recreate them from the information we had available before, + // plus adding "no-op" implementations of all the functions not available before v1 + 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(); + if (p->required_api_version().m_version_minor < 1) + { + res->reader_ext->get_table_name = res->reader.get_table_name; + res->reader_ext->get_table_size = res->reader.get_table_size; + res->reader_ext->get_table_entry = res->reader.get_table_entry; + res->reader_ext->read_entry_field = res->reader.read_entry_field; + res->reader_ext->release_table_entry = noop_release_table_entry; + res->reader_ext->iterate_entries = noop_iterate_entries; + + res->writer_ext->clear_table = res->writer.clear_table; + res->writer_ext->erase_table_entry = res->writer.erase_table_entry; + res->writer_ext->create_table_entry = res->writer.create_table_entry; + res->writer_ext->destroy_table_entry = res->writer.destroy_table_entry; + res->writer_ext->add_table_entry = res->writer.add_table_entry; + res->writer_ext->write_entry_field = res->writer.write_entry_field; + + res->fields_ext->list_table_fields = res->fields.list_table_fields; + res->fields_ext->get_table_field = res->fields.get_table_field; + res->fields_ext->add_table_field = res->fields.add_table_field; + } + else + { + if (!in->reader_ext || !in->writer_ext || !in->fields_ext) + { + throw sinsp_exception(errprefix + "extended vtables must all be defined"); + } + + res->reader_ext->get_table_name = in->reader_ext->get_table_name; + res->reader_ext->get_table_size = in->reader_ext->get_table_size; + res->reader_ext->get_table_entry = in->reader_ext->get_table_entry; + res->reader_ext->read_entry_field = in->reader_ext->read_entry_field; + res->reader_ext->release_table_entry = in->reader_ext->release_table_entry; + res->reader_ext->iterate_entries = in->reader_ext->iterate_entries; + + res->writer_ext->clear_table = in->writer_ext->clear_table; + res->writer_ext->erase_table_entry = in->writer_ext->erase_table_entry; + res->writer_ext->create_table_entry = in->writer_ext->create_table_entry; + res->writer_ext->destroy_table_entry = in->writer_ext->destroy_table_entry; + res->writer_ext->add_table_entry = in->writer_ext->add_table_entry; + res->writer_ext->write_entry_field = in->writer_ext->write_entry_field; + + res->fields_ext->list_table_fields = in->fields_ext->list_table_fields; + res->fields_ext->get_table_field = in->fields_ext->get_table_field; + res->fields_ext->add_table_field = in->fields_ext->add_table_field; + } + + if ((!res->reader_ext->get_table_name || res->reader_ext->get_table_name != res->reader.get_table_name) || + (!res->reader_ext->get_table_size || res->reader_ext->get_table_size != res->reader.get_table_size) || + (!res->reader_ext->get_table_entry || res->reader_ext->get_table_entry != res->reader.get_table_entry) || + (!res->reader_ext->read_entry_field || res->reader_ext->read_entry_field != res->reader.read_entry_field) || + !res->reader_ext->release_table_entry || + !res->reader_ext->iterate_entries) + { + throw sinsp_exception(errprefix + "broken or inconsistent reader vtables"); + } + + if((!res->writer_ext->clear_table || res->writer_ext->clear_table != res->writer.clear_table) || + (!res->writer_ext->erase_table_entry || res->writer_ext->erase_table_entry != res->writer.erase_table_entry) || + (!res->writer_ext->create_table_entry || res->writer_ext->create_table_entry != res->writer.create_table_entry) || + (!res->writer_ext->destroy_table_entry || res->writer_ext->destroy_table_entry != res->writer.destroy_table_entry) || + (!res->writer_ext->add_table_entry || res->writer_ext->add_table_entry != res->writer.add_table_entry) || + (!res->writer_ext->write_entry_field || res->writer_ext->write_entry_field != res->writer.write_entry_field)) + { + throw sinsp_exception(errprefix + "broken or inconsistent writer vtables"); + } + + if((!res->fields_ext->list_table_fields || res->fields_ext->list_table_fields != res->fields.list_table_fields) || + (!res->fields_ext->get_table_field || res->fields_ext->get_table_field != res->fields.get_table_field) || + (!res->fields_ext->add_table_field || res->fields_ext->add_table_field != res->fields.add_table_field)) + { + throw sinsp_exception(errprefix + "broken or inconsistent fields vtables"); + } + + return res; +} static inline std::string table_input_error_prefix(const sinsp_plugin* o, ss_plugin_table_input* i) { @@ -112,7 +239,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { plugin_field_infos( const sinsp_plugin* o, - const std::shared_ptr& i) + 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; @@ -121,14 +248,14 @@ struct plugin_table_wrapper: public libsinsp::state::table virtual ~plugin_field_infos() = default; const sinsp_plugin* m_owner; - std::shared_ptr m_input; + 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.list_table_fields(m_input->table, &nfields); + 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()); @@ -171,7 +298,7 @@ struct plugin_table_wrapper: public libsinsp::state::table } if (m_accessors[f.index()] == nullptr) { - auto facc = m_input->fields.get_table_field(m_input->table, f.name().c_str(), typeinfo_to_state_type(f.info())); + 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()); @@ -184,7 +311,7 @@ struct plugin_table_wrapper: public libsinsp::state::table virtual const ds::field_info& add_field(const ds::field_info& field) override { - auto ret = m_input->fields.add_table_field(m_input->table, field.name().c_str(), typeinfo_to_state_type(field.info())); + 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()); @@ -207,7 +334,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { plugin_table_entry( const sinsp_plugin* o, - const std::shared_ptr& i, + const owned_table_input_t& i, const std::shared_ptr& fields, ss_plugin_table_entry_t* e, bool detached) @@ -269,12 +396,12 @@ struct plugin_table_wrapper: public libsinsp::state::table // then it must be destroyed here. if (m_detached) { - m_input->writer.destroy_table_entry(m_input->table, m_entry); + m_input->writer_ext->destroy_table_entry(m_input->table, m_entry); } }; const sinsp_plugin* m_owner; - std::shared_ptr m_input; + owned_table_input_t m_input; ss_plugin_table_entry_t* m_entry; bool m_detached; @@ -295,7 +422,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { const auto& infos = get_plugin_field_infos(); ss_plugin_state_data dout; - auto rc = m_input->reader.read_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &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()); @@ -345,7 +472,7 @@ struct plugin_table_wrapper: public libsinsp::state::table #undef _X } - auto rc = m_input->writer.write_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &v); + 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()); @@ -368,7 +495,7 @@ struct plugin_table_wrapper: public libsinsp::state::table plugin_table_wrapper(const sinsp_plugin* o, const ss_plugin_table_input* i) : libsinsp::state::table(i->name, ss::field_infos()), m_owner(o), - m_input(std::make_shared(*i)), + m_input(copy_and_check_table_input(o, i)), m_static_fields(), m_dyn_fields(std::make_shared(o, m_input)) { @@ -386,7 +513,7 @@ struct plugin_table_wrapper: public libsinsp::state::table plugin_table_wrapper& operator = (const plugin_table_wrapper& s) = delete; const sinsp_plugin* m_owner; - std::shared_ptr m_input; + owned_table_input_t m_input; libsinsp::state::static_struct::field_infos m_static_fields; std::shared_ptr m_dyn_fields; @@ -404,7 +531,7 @@ struct plugin_table_wrapper: public libsinsp::state::table size_t entries_count() const override { - auto res = m_input->reader.get_table_size(m_input->table); + 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()); @@ -414,7 +541,7 @@ struct plugin_table_wrapper: public libsinsp::state::table void clear_entries() override { - auto res = m_input->writer.clear_table(m_input->table); + 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()); @@ -423,12 +550,13 @@ struct plugin_table_wrapper: public libsinsp::state::table bool foreach_entry(std::function pred) override { + // TODO throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "foreach operation not supported by plugin api"); } std::unique_ptr new_entry() const override { - auto res = m_input->writer.create_table_entry(m_input->table); + 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()); @@ -440,7 +568,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { ss_plugin_state_data keydata; get_key_as_data(key, keydata); - auto res = m_input->reader.get_table_entry(m_input->table, &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 @@ -461,7 +589,7 @@ struct plugin_table_wrapper: public libsinsp::state::table ss_plugin_state_data keydata; get_key_as_data(key, keydata); - auto res = m_input->writer.add_table_entry(m_input->table, &keydata, entry->m_entry); + 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()); @@ -475,7 +603,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { ss_plugin_state_data keydata; get_key_as_data(key, keydata); - auto res = m_input->writer.erase_table_entry(m_input->table, &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; @@ -609,7 +737,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->fields.list_table_fields(pt, nfields); + 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(); @@ -648,7 +776,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->fields.get_table_field(pt, name, data_type); + 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(); @@ -732,7 +860,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->fields.add_table_field(pt, name, data_type); + 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(); @@ -781,7 +909,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->reader.get_table_size(pt); + 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(); @@ -802,7 +930,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->reader.get_table_entry(pt, key); + 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(); @@ -829,6 +957,17 @@ struct sinsp_table_wrapper 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); + static void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) + { + // TODO + } + + 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) + { + // TODO + return false; + } + static ss_plugin_rc clear(ss_plugin_table_t* _t) { auto t = static_cast(_t); @@ -836,7 +975,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer.clear_table(pt); + 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(); @@ -858,7 +997,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer.erase_table_entry(pt, key); + 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(); @@ -892,7 +1031,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer.create_table_entry(pt); + 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(); @@ -920,7 +1059,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - t->m_table_plugin_input->writer.destroy_table_entry(pt, _e); + t->m_table_plugin_input->writer_ext->destroy_table_entry(pt, _e); } #define _X(_type, _dtype) \ @@ -942,7 +1081,7 @@ struct sinsp_table_wrapper if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer.add_table_entry(pt, key, _e); + 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(); @@ -975,7 +1114,7 @@ ss_plugin_rc sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, ss_plu if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->reader.read_entry_field(pt, _e, f, out); + 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(); @@ -1013,7 +1152,7 @@ ss_plugin_rc sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, ss_pl if (t->m_table_plugin_input) { auto pt = t->m_table_plugin_input->table; - auto ret = t->m_table_plugin_input->writer.write_entry_field(pt, _e, f, in); + 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(); @@ -1052,104 +1191,131 @@ ss_plugin_rc sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, ss_pl static ss_plugin_table_fieldinfo* dispatch_list_fields(ss_plugin_table_t *_t, uint32_t *nfields) { auto t = static_cast(_t); - return t->fields.list_table_fields(t->table, nfields); + return t->fields_ext->list_table_fields(t->table, nfields); } static ss_plugin_table_field_t* dispatch_get_field(ss_plugin_table_t* _t, const char* name, ss_plugin_state_type data_type) { auto t = static_cast(_t); - return t->fields.get_table_field(t->table, name, data_type); + return t->fields_ext->get_table_field(t->table, name, data_type); } static ss_plugin_table_field_t* dispatch_add_field(ss_plugin_table_t* _t, const char* name, ss_plugin_state_type data_type) { auto t = static_cast(_t); - return t->fields.add_table_field(t->table, name, data_type); + return t->fields_ext->add_table_field(t->table, name, data_type); } static const char* dispatch_get_name(ss_plugin_table_t* _t) { auto t = static_cast(_t); - return t->reader.get_table_name(t->table); + return t->reader_ext->get_table_name(t->table); } static uint64_t dispatch_get_size(ss_plugin_table_t* _t) { auto t = static_cast(_t); - return t->reader.get_table_size(t->table); + return t->reader_ext->get_table_size(t->table); } static ss_plugin_table_entry_t* dispatch_get_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key) { auto t = static_cast(_t); - return t->reader.get_table_entry(t->table, key); + return t->reader_ext->get_table_entry(t->table, key); } static ss_plugin_rc dispatch_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->reader.read_entry_field(t->table, e, f, out); + return t->reader_ext->read_entry_field(t->table, e, f, out); +} + +static void dispatch_release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* e) +{ + auto t = static_cast(_t); + t->reader_ext->release_table_entry(t->table, e); +} + +static ss_plugin_bool dispatch_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->reader_ext->iterate_entries(t->table, it, s); } static ss_plugin_rc dispatch_clear(ss_plugin_table_t* _t) { auto t = static_cast(_t); - return t->writer.clear_table(t->table); + return t->writer_ext->clear_table(t->table); } static ss_plugin_rc dispatch_erase_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key) { auto t = static_cast(_t); - return t->writer.erase_table_entry(t->table, key); + return t->writer_ext->erase_table_entry(t->table, key); } static ss_plugin_table_entry_t* dispatch_create_table_entry(ss_plugin_table_t* _t) { auto t = static_cast(_t); - return t->writer.create_table_entry(t->table); + return t->writer_ext->create_table_entry(t->table); } static void dispatch_destroy_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* e) { auto t = static_cast(_t); - return t->writer.destroy_table_entry(t->table, e); + return t->writer_ext->destroy_table_entry(t->table, e); } static ss_plugin_table_entry_t* dispatch_add_entry(ss_plugin_table_t* _t, const ss_plugin_state_data* key, ss_plugin_table_entry_t* entry) { auto t = static_cast(_t); - return t->writer.add_table_entry(t->table, key, entry); + return t->writer_ext->add_table_entry(t->table, key, entry); } static ss_plugin_rc dispatch_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->writer.write_entry_field(t->table, e, f, in); + return t->writer_ext->write_entry_field(t->table, e, f, in); } -void sinsp_plugin::table_field_api(ss_plugin_table_fields_vtable& out) +void sinsp_plugin::table_field_api(ss_plugin_table_fields_vtable& out, ss_plugin_table_fields_vtable_ext& extout) { - out.list_table_fields = dispatch_list_fields; - out.add_table_field = dispatch_add_field; - out.get_table_field = dispatch_get_field; + extout.list_table_fields = dispatch_list_fields; + extout.add_table_field = dispatch_add_field; + extout.get_table_field = dispatch_get_field; + out.list_table_fields = extout.list_table_fields; + out.add_table_field = extout.add_table_field; + out.get_table_field = extout.get_table_field; } -void sinsp_plugin::table_read_api(ss_plugin_table_reader_vtable& out) +void sinsp_plugin::table_read_api(ss_plugin_table_reader_vtable& out, ss_plugin_table_reader_vtable_ext& extout) { - out.get_table_name = dispatch_get_name; - out.get_table_size = dispatch_get_size; - out.get_table_entry = dispatch_get_entry; - out.read_entry_field = dispatch_read_entry_field; + extout.get_table_name = dispatch_get_name; + extout.get_table_size = dispatch_get_size; + extout.get_table_entry = dispatch_get_entry; + extout.read_entry_field = dispatch_read_entry_field; + extout.release_table_entry = dispatch_release_table_entry; + extout.iterate_entries = dispatch_iterate_entries; + out.get_table_name = extout.get_table_name; + out.get_table_size = extout.get_table_size; + out.get_table_entry = extout.get_table_entry; + out.read_entry_field = extout.read_entry_field; } -void sinsp_plugin::table_write_api(ss_plugin_table_writer_vtable& out) +void sinsp_plugin::table_write_api(ss_plugin_table_writer_vtable& out, ss_plugin_table_writer_vtable_ext& extout) { - out.clear_table = dispatch_clear; - out.erase_table_entry = dispatch_erase_entry; - out.create_table_entry = dispatch_create_table_entry; - out.destroy_table_entry = dispatch_destroy_table_entry; - out.add_table_entry = dispatch_add_entry; - out.write_entry_field = dispatch_write_entry_field; + extout.clear_table = dispatch_clear; + extout.erase_table_entry = dispatch_erase_entry; + extout.create_table_entry = dispatch_create_table_entry; + extout.destroy_table_entry = dispatch_destroy_table_entry; + extout.add_table_entry = dispatch_add_entry; + extout.write_entry_field = dispatch_write_entry_field; + out.clear_table = extout.clear_table; + out.erase_table_entry = extout.erase_table_entry; + out.create_table_entry = extout.create_table_entry; + out.destroy_table_entry = extout.destroy_table_entry; + out.add_table_entry = extout.add_table_entry; + out.write_entry_field = extout.write_entry_field; } ss_plugin_table_info* sinsp_plugin::table_api_list_tables(ss_plugin_owner_t* o, uint32_t* ntables) @@ -1171,9 +1337,12 @@ ss_plugin_table_info* sinsp_plugin::table_api_list_tables(ss_plugin_owner_t* o, return NULL; } -void sinsp_plugin::table_input_deleter::operator()(ss_plugin_table_input* in) +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; } @@ -1194,24 +1363,42 @@ 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(), table_input_deleter()); \ + 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->fields.list_table_fields = sinsp_table_wrapper::list_fields; \ - res->fields.add_table_field = sinsp_table_wrapper::add_field; \ - res->fields.get_table_field = sinsp_table_wrapper::get_field; \ - res->reader.get_table_name = sinsp_table_wrapper::get_name; \ - res->reader.get_table_size = sinsp_table_wrapper::get_size; \ - res->reader.get_table_entry = sinsp_table_wrapper::get_entry; \ - res->reader.read_entry_field = sinsp_table_wrapper::read_entry_field; \ - res->writer.clear_table = sinsp_table_wrapper::clear; \ - res->writer.erase_table_entry = sinsp_table_wrapper::erase_entry; \ - res->writer.create_table_entry = sinsp_table_wrapper::create_table_entry; \ - res->writer.destroy_table_entry = sinsp_table_wrapper::destroy_table_entry; \ - res->writer.add_table_entry = sinsp_table_wrapper::add_entry; \ - res->writer.write_entry_field = sinsp_table_wrapper::write_entry_field; \ + 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(); \ }; From 10947be025c94e4bedfba027fb42008826f5ec34 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 16:21:09 +0000 Subject: [PATCH 39/66] update(userspace/libsinsp): support plugin tables iteration and entry release Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 83 ++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index d6bf585b10..eb0701a983 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -333,7 +333,7 @@ struct plugin_table_wrapper: public libsinsp::state::table struct plugin_table_entry: public libsinsp::state::table_entry { plugin_table_entry( - const sinsp_plugin* o, + sinsp_plugin* o, const owned_table_input_t& i, const std::shared_ptr& fields, ss_plugin_table_entry_t* e, @@ -400,7 +400,7 @@ struct plugin_table_wrapper: public libsinsp::state::table } }; - const sinsp_plugin* m_owner; + sinsp_plugin* m_owner; owned_table_input_t m_input; ss_plugin_table_entry_t* m_entry; bool m_detached; @@ -492,7 +492,7 @@ struct plugin_table_wrapper: public libsinsp::state::table } }; - plugin_table_wrapper(const sinsp_plugin* o, const ss_plugin_table_input* i) + plugin_table_wrapper(sinsp_plugin* o, const ss_plugin_table_input* i) : libsinsp::state::table(i->name, ss::field_infos()), m_owner(o), m_input(copy_and_check_table_input(o, i)), @@ -512,7 +512,7 @@ struct plugin_table_wrapper: public libsinsp::state::table plugin_table_wrapper(const plugin_table_wrapper& s) = delete; plugin_table_wrapper& operator = (const plugin_table_wrapper& s) = delete; - const sinsp_plugin* m_owner; + sinsp_plugin* m_owner; owned_table_input_t m_input; libsinsp::state::static_struct::field_infos m_static_fields; std::shared_ptr m_dyn_fields; @@ -548,10 +548,41 @@ struct plugin_table_wrapper: public libsinsp::state::table } } - bool foreach_entry(std::function pred) override + // 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 { - // TODO - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "foreach operation not supported by plugin api"); + 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) + { + if (!state.err.empty()) + { + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "iterate entries failure: " + state.err); + } + return false; + } + return true; } std::unique_ptr new_entry() const override @@ -959,12 +990,46 @@ struct sinsp_table_wrapper static void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) { - // TODO + 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; + } + + // 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? } 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) { - // TODO + 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::function iter = [it, s](auto& e) + { + return it(s, static_cast(&e)) != 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; } From 20553d034a760bef286041f23e428ab6b7835aa5 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 16:21:33 +0000 Subject: [PATCH 40/66] test(userspace/libsinsp): cover plugin tables iteration and release Signed-off-by: Jason Dellaluce --- userspace/libsinsp/test/plugins.ut.cpp | 26 +- .../libsinsp/test/plugins/sample_table.h | 65 ++- userspace/libsinsp/test/plugins/tables.cpp | 500 +++++++++++------- 3 files changed, 389 insertions(+), 202 deletions(-) diff --git a/userspace/libsinsp/test/plugins.ut.cpp b/userspace/libsinsp/test/plugins.ut.cpp index 5d55dae2ce..fe2afb8fa7 100644 --- a/userspace/libsinsp/test/plugins.ut.cpp +++ b/userspace/libsinsp/test/plugins.ut.cpp @@ -528,8 +528,30 @@ TEST_F(sinsp_with_test_input, plugin_tables) ASSERT_EQ(tmpstr, "hello"); } - // the plugin API does not support this yet - ASSERT_ANY_THROW(table->foreach_entry([](auto& e) { return true; })); + // full iteration + auto it = [&](libsinsp::state::table_entry& e) -> bool + { + uint64_t tmpu64; + std::string tmpstr; + e.get_dynamic_field(sfieldacc, tmpu64); + EXPECT_EQ(tmpu64, 5); + e.get_dynamic_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; + })); + + // iteration with error + ASSERT_ANY_THROW(table->foreach_entry([&](libsinsp::state::table_entry& e) -> bool + { + throw sinsp_exception("some error"); + })); // erasing an unknown thread ASSERT_EQ(table->erase_entry(max_iterations), false); diff --git a/userspace/libsinsp/test/plugins/sample_table.h b/userspace/libsinsp/test/plugins/sample_table.h index ecb73145f4..7035280b4d 100644 --- a/userspace/libsinsp/test/plugins/sample_table.h +++ b/userspace/libsinsp/test/plugins/sample_table.h @@ -157,6 +157,24 @@ class sample_table return SS_PLUGIN_SUCCESS; } + static void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) + { + // there's no resource to release when an entry becomes unused + } + + 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) + { + auto t = static_cast(_t); + for (auto& [k, e]: t->entries) + { + if (it(s, static_cast(&e)) != 1) + { + return 0; + } + } + return 1; + } + static ss_plugin_rc clear(ss_plugin_table_t *_t) { auto t = static_cast(_t); @@ -236,19 +254,37 @@ class sample_table ret->name = t->name.c_str(); ret->table = t; ret->key_type = ss_plugin_state_type::SS_PLUGIN_ST_UINT64; - ret->fields.list_table_fields = list_fields; - ret->fields.get_table_field = get_field; - ret->fields.add_table_field = add_field; - ret->reader.get_table_name = get_name; - ret->reader.get_table_size = get_size; - ret->reader.get_table_entry = get_entry; - ret->reader.read_entry_field = read_entry_field; - ret->writer.clear_table = clear; - ret->writer.erase_table_entry = erase_entry; - ret->writer.create_table_entry = create_entry; - ret->writer.destroy_table_entry = destroy_entry; - ret->writer.add_table_entry = add_entry; - ret->writer.write_entry_field = write_entry_field; + ret->reader_ext = &t->reader_vtable; + ret->writer_ext = &t->writer_vtable; + ret->fields_ext = &t->fields_vtable; + ret->fields_ext->list_table_fields = list_fields; + ret->fields_ext->get_table_field = get_field; + ret->fields_ext->add_table_field = add_field; + ret->fields.list_table_fields = ret->fields_ext->list_table_fields; + ret->fields.get_table_field = ret->fields_ext->get_table_field; + ret->fields.add_table_field = ret->fields_ext->add_table_field; + ret->reader_ext->get_table_name = get_name; + ret->reader_ext->get_table_size = get_size; + ret->reader_ext->get_table_entry = get_entry; + ret->reader_ext->read_entry_field = read_entry_field; + ret->reader_ext->release_table_entry = release_table_entry; + ret->reader_ext->iterate_entries = iterate_entries; + ret->reader.get_table_name = ret->reader_ext->get_table_name; + ret->reader.get_table_size = ret->reader_ext->get_table_size; + ret->reader.get_table_entry = ret->reader_ext->get_table_entry; + ret->reader.read_entry_field = ret->reader_ext->read_entry_field; + ret->writer_ext->clear_table = clear; + ret->writer_ext->erase_table_entry = erase_entry; + ret->writer_ext->create_table_entry = create_entry; + ret->writer_ext->destroy_table_entry = destroy_entry; + ret->writer_ext->add_table_entry = add_entry; + ret->writer_ext->write_entry_field = write_entry_field; + ret->writer.clear_table = ret->writer_ext->clear_table; + ret->writer.erase_table_entry = ret->writer_ext->erase_table_entry; + ret->writer.create_table_entry = ret->writer_ext->create_table_entry; + ret->writer.destroy_table_entry = ret->writer_ext->destroy_table_entry; + ret->writer.add_table_entry = ret->writer_ext->add_table_entry; + ret->writer.write_entry_field = ret->writer_ext->write_entry_field; return ret; } @@ -258,4 +294,7 @@ class sample_table std::vector strings; std::unordered_map entries; std::vector fields; + ss_plugin_table_reader_vtable_ext reader_vtable; + ss_plugin_table_writer_vtable_ext writer_vtable; + ss_plugin_table_fields_vtable_ext fields_vtable; }; diff --git a/userspace/libsinsp/test/plugins/tables.cpp b/userspace/libsinsp/test/plugins/tables.cpp index 894dbd1c8e..2965bf35c5 100644 --- a/userspace/libsinsp/test/plugins/tables.cpp +++ b/userspace/libsinsp/test/plugins/tables.cpp @@ -167,235 +167,361 @@ static const char* plugin_get_last_error(ss_plugin_t* s) // parses events and keeps a count for each thread about the syscalls of the open family static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_input *ev, const ss_plugin_event_parse_input* in) { + static int64_t s_new_thread_tid = 999999; + int step = 0; ss_plugin_state_data tmp; + ss_plugin_table_entry_t* thread; plugin_state *ps = (plugin_state *) s; // get table name - if (strcmp("threads", in->table_reader.get_table_name(ps->thread_table))) + step++; { - fprintf(stderr, "table_reader.get_table_name (1) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + if (strcmp("threads", in->table_reader_ext->get_table_name(ps->thread_table))) + { + fprintf(stderr, "table_reader.get_table_name (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } } // check that the table contains only the init thread - auto size = in->table_reader.get_table_size(ps->thread_table); - if (size != 1) + step++; { - fprintf(stderr, "table_reader.get_table_size (2) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + auto size = in->table_reader_ext->get_table_size(ps->thread_table); + if (size != 1) + { + fprintf(stderr, "table_reader.get_table_size (%d) failure: (%lu) %s\n", step, size, in->get_owner_last_error(in->owner)); + exit(1); + } } // get the init thread and read its comm - tmp.s64 = 1; - auto thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); - if (!thread) - { - fprintf(stderr, "table_reader.get_table_entry (3) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (4) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("init", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (4) inconsistency\n"); - exit(1); + step++; + { + tmp.s64 = 1; + thread = in->table_reader_ext->get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("init", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } } // read-write dynamic field from existing thread - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (5) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 0) - { - fprintf(stderr, "table_reader.read_entry_field (5) inconsistency\n"); - exit(1); - } - tmp.u64 = 5; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (6) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (7) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 5) - { - fprintf(stderr, "table_reader.read_entry_field (7) inconsistency\n"); - exit(1); - } - tmp.u64 = 0; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (8) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - - // read-write dynamic field (str from existing thread - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (9) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (9) inconsistency\n"); - exit(1); - } - tmp.str = "hello"; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (10) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (11) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("hello", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (11) inconsistency\n"); - exit(1); - } - tmp.str = ""; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (12) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + step++; + { + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 0) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } + tmp.u64 = 5; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } + } + + // read-write dynamic field (str) from existing thread + step++; + { + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } + tmp.str = "hello"; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("hello", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } } // get non-existing thread - tmp.s64 = 10000; - thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); - if (thread) + step++; { - fprintf(stderr, "table_reader.get_table_entry (13) inconsistency\n"); - exit(1); + tmp.s64 = s_new_thread_tid; + thread = in->table_reader_ext->get_table_entry(ps->thread_table, &tmp); + if (thread) + { + fprintf(stderr, "table_reader.get_table_entry (%d) inconsistency\n", step); + exit(1); + } } // creating a destroying a thread without adding it to the table - thread = in->table_writer.create_table_entry(ps->thread_table); - if (!thread) + step++; { - fprintf(stderr, "table_reader.create_table_entry (14) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + thread = in->table_writer_ext->create_table_entry(ps->thread_table); + if (!thread) + { + fprintf(stderr, "table_reader.create_table_entry (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + in->table_writer_ext->destroy_table_entry(ps->thread_table, thread); } - in->table_writer.destroy_table_entry(ps->thread_table, thread); // creating and adding a thread to the table - thread = in->table_writer.create_table_entry(ps->thread_table); - if (!thread) - { - fprintf(stderr, "table_reader.create_table_entry (15) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - tmp.s64 = 10000; - thread = in->table_writer.add_table_entry(ps->thread_table, &tmp, thread); - if (!thread) - { - fprintf(stderr, "table_reader.add_table_entry (16) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - size = in->table_reader.get_table_size(ps->thread_table); - if (size != 2) - { - fprintf(stderr, "table_reader.get_table_size (17) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + step++; + { + thread = in->table_writer_ext->create_table_entry(ps->thread_table); + if (!thread) + { + fprintf(stderr, "table_reader.create_table_entry (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + tmp.s64 = s_new_thread_tid; + thread = in->table_writer_ext->add_table_entry(ps->thread_table, &tmp, thread); + if (!thread) + { + fprintf(stderr, "table_reader.add_table_entry (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + auto size = in->table_reader_ext->get_table_size(ps->thread_table); + if (size != 2) + { + 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); } // get newly-created thread - tmp.s64 = 10000; - thread = in->table_reader.get_table_entry(ps->thread_table, &tmp); - if (!thread) + step++; { - fprintf(stderr, "table_reader.get_table_entry (18) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + tmp.s64 = s_new_thread_tid; + thread = in->table_reader_ext->get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } } // read and write from newly-created thread (static field) - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (19) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (19) inconsistency\n"); - exit(1); - } - tmp.str = "hello"; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (20) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (21) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (strcmp("hello", tmp.str)) - { - fprintf(stderr, "table_reader.read_entry_field (21) inconsistency\n"); - exit(1); + step++; + { + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } + tmp.str = "hello"; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->write_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_static_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (strcmp("hello", tmp.str)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } } // read and write from newly-created thread (dynamic field) - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (22) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 0) - { - fprintf(stderr, "table_reader.read_entry_field (22) inconsistency\n"); - exit(1); - } - tmp.u64 = 5; - if (SS_PLUGIN_SUCCESS != in->table_writer.write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.write_entry_field (23) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (SS_PLUGIN_SUCCESS != in->table_reader.read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) - { - fprintf(stderr, "table_reader.read_entry_field (24) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - if (tmp.u64 != 5) - { - fprintf(stderr, "table_reader.read_entry_field (24) inconsistency\n"); - exit(1); + step++; + { + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 0) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } + tmp.u64 = 5; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (SS_PLUGIN_SUCCESS != in->table_reader_ext->read_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + if (tmp.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", step); + exit(1); + } + + tmp.s64 = s_new_thread_tid; + in->table_reader_ext->release_table_entry(ps->thread_table, &tmp); } // erasing an unknown thread - tmp.s64 = 10; - if (SS_PLUGIN_SUCCESS == in->table_writer.erase_table_entry(ps->thread_table, &tmp)) - { - fprintf(stderr, "table_reader.erase_table_entry (25) inconsistency\n"); - exit(1); + step++; + { + tmp.s64 = 10; + if (SS_PLUGIN_SUCCESS == in->table_writer_ext->erase_table_entry(ps->thread_table, &tmp)) + { + fprintf(stderr, "table_reader.erase_table_entry (%d) inconsistency\n", step); + exit(1); + } + } + + // loop over all threads, we expect to only find two (init, and our new one) + step++; + { + struct iterate_entries_state + { + int* step = nullptr; + uint64_t count = 0; + const ss_plugin_event_parse_input *in = nullptr; + plugin_state* ps = nullptr; + }; + + // successful iteration + iterate_entries_state its1; + its1.in = in; + its1.ps = ps; + its1.step = &step; + auto it1 = [](ss_plugin_table_iterator_state_t* s, ss_plugin_table_entry_t* e) -> ss_plugin_bool + { + auto st = (iterate_entries_state*) s; + st->count++; + + ss_plugin_state_data val; + if (SS_PLUGIN_SUCCESS != st->in->table_reader_ext->read_entry_field(st->ps->thread_table, e, st->ps->thread_static_field, &val)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", (*st->step), st->in->get_owner_last_error(st->in->owner)); + exit(1); + } + if (strcmp(val.str, "init") == 0 || strcmp(val.str, "hello") == 0) + { + if (SS_PLUGIN_SUCCESS != st->in->table_reader_ext->read_entry_field(st->ps->thread_table, e, st->ps->thread_dynamic_field, &val)) + { + fprintf(stderr, "table_reader.read_entry_field (%d) failure: %s\n", (*st->step), st->in->get_owner_last_error(st->in->owner)); + exit(1); + } + if (val.u64 != 5) + { + fprintf(stderr, "table_reader.read_entry_field (%d) inconsistency\n", (*st->step)); + exit(1); + } + } + else + { + fprintf(stderr, "table_reader.read_entry_field (%d) unexpected value: %s\n", (*st->step), val.str); + exit(1); + } + + return 1; + }; + if (in->table_reader_ext->iterate_entries(ps->thread_table, it1, (ss_plugin_table_iterator_state_t*) &its1) != 1) + { + fprintf(stderr, "table_reader.iterate_entries (%d) unexpected break-out\n", step); + exit(1); + } + if (its1.count != 2) + { + fprintf(stderr, "table_reader.iterate_entries (%d) unexpected count result\n", step); + exit(1); + } + + // iteration with break-out + auto it2 = [](ss_plugin_table_iterator_state_t* s, ss_plugin_table_entry_t* e) -> ss_plugin_bool + { + return false; + }; + if (in->table_reader_ext->iterate_entries(ps->thread_table, it2, (ss_plugin_table_iterator_state_t*) &its1) != 0) + { + fprintf(stderr, "table_reader.iterate_entries (%d) break-out was expected\n", step); + exit(1); + } } // erase newly-created thread - tmp.s64 = 10000; - if (SS_PLUGIN_SUCCESS != in->table_writer.erase_table_entry(ps->thread_table, &tmp)) - { - fprintf(stderr, "table_reader.erase_table_entry (26) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); - } - size = in->table_reader.get_table_size(ps->thread_table); - if (size != 1) - { - fprintf(stderr, "table_reader.get_table_size (27) failure: %s\n", in->get_owner_last_error(in->owner)); - exit(1); + step++; + { + tmp.s64 = s_new_thread_tid; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->erase_table_entry(ps->thread_table, &tmp)) + { + fprintf(stderr, "table_reader.erase_table_entry (%d) inconsistency\n", step); + exit(1); + } + } + + // revert edits made to existing thread + step++; + { + tmp.s64 = 1; + thread = in->table_reader_ext->get_table_entry(ps->thread_table, &tmp); + if (!thread) + { + fprintf(stderr, "table_reader.get_table_entry (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + tmp.u64 = 0; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field, &tmp)) + { + fprintf(stderr, "table_reader.write_entry_field (%d) failure: %s\n", step, in->get_owner_last_error(in->owner)); + exit(1); + } + tmp.str = ""; + if (SS_PLUGIN_SUCCESS != in->table_writer_ext->write_entry_field(ps->thread_table, thread, ps->thread_dynamic_field_str, &tmp)) + { + 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); } return SS_PLUGIN_SUCCESS; From 1fcafbf54fc3cae7312681928d5501556cb4d5a4 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 17:29:52 +0000 Subject: [PATCH 41/66] fix(userspace/libsinsp): support releasing table entries Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 137 ++++++++---------- .../libsinsp/test/plugins/sample_table.h | 20 ++- .../libsinsp/test/plugins/syscall_extract.cpp | 6 + .../libsinsp/test/plugins/syscall_parse.cpp | 6 + userspace/plugin/plugin_api.h | 3 +- 5 files changed, 91 insertions(+), 81 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index eb0701a983..afa676f53b 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -94,7 +94,7 @@ template<> inline void convert_types(const std::string& from, const char*& to) } static void noop_release_table_entry(ss_plugin_table_t*, ss_plugin_table_entry_t*) -{ +{ } static ss_plugin_bool noop_iterate_entries(ss_plugin_table_t*, ss_plugin_table_iterator_func_t, ss_plugin_table_iterator_state_t*) @@ -332,78 +332,57 @@ struct plugin_table_wrapper: public libsinsp::state::table struct plugin_table_entry: public libsinsp::state::table_entry { + struct ptr_wrap + { + ptr_wrap( + sinsp_plugin* o, + const owned_table_input_t& i, + ss_plugin_table_entry_t* e, + bool detached): + m_owner(o), + m_input(i), + m_entry(e), + m_detached(detached) {}; + ptr_wrap(ptr_wrap&&) = default; + ptr_wrap& operator = (ptr_wrap&&) = default; + ptr_wrap(const ptr_wrap& s) = default; + ptr_wrap& operator = (const ptr_wrap& s) = default; + virtual ~ptr_wrap() + { + 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; + }; + 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) - { - // note: this is not supposed to ever happen. In the general case - // m_detached will be false because plugin_table_entry will not - // own the entry (it will be held by the table itself). - // The only time in which it is owner is when being held in a - // unique pointer during the new_entry -> add_entry/destroy_entry flow. - ASSERT(!o.m_detached); - if (o.m_detached) - { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "entry can't be copied while being detached"); - } - m_owner = o.m_owner; - m_input = o.m_input; - m_entry = o.m_entry; - m_detached = o.m_detached; - }; - plugin_table_entry& operator = (const plugin_table_entry& o) - { - ASSERT(!o.m_detached); - if (o.m_detached) - { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "entry can't be copied while being detached"); - } - m_owner = o.m_owner; - m_input = o.m_input; - m_entry = o.m_entry; - m_detached = o.m_detached; - } - plugin_table_entry(plugin_table_entry&& o) - { - m_owner = o.m_owner; - m_input = o.m_input; - m_entry = o.m_entry; - m_detached = o.m_detached; - o.m_detached = false; - }; - plugin_table_entry& operator = (plugin_table_entry&& o) - { - m_owner = o.m_owner; - m_input = o.m_input; - m_entry = o.m_entry; - m_detached = o.m_detached; - o.m_detached = false; - return *this; - }; - virtual ~plugin_table_entry() - { - // if this gets destroyed when holding a detached entry pointer - // (one that is allocated, but not yet inserted in the table), - // then it must be destroyed here. - if (m_detached) - { - m_input->writer_ext->destroy_table_entry(m_input->table, m_entry); - } - }; + bool detached): + table_entry(fields), + m_ptr(std::make_shared(o, i, e, detached)) {}; + plugin_table_entry(const plugin_table_entry& o) = default; + plugin_table_entry& operator = (const plugin_table_entry& o) = default; + plugin_table_entry(plugin_table_entry&& o) = default; + plugin_table_entry& operator = (plugin_table_entry&& o) = default; + virtual ~plugin_table_entry() = default; - sinsp_plugin* m_owner; - owned_table_input_t m_input; - ss_plugin_table_entry_t* m_entry; - bool m_detached; + std::shared_ptr m_ptr; // note(jasondellaluce): dynamic cast is expensive but this is not expected // to ever be ever invoked, because we set the fields shared pointer @@ -413,7 +392,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { 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"); + throw sinsp_exception(table_input_error_prefix(m_ptr->m_owner, m_ptr->m_input.get()) + "plugin table can only be set with plugin dynamic fields"); } table_entry::set_dynamic_fields(defs); } @@ -422,10 +401,10 @@ struct plugin_table_wrapper: public libsinsp::state::table { 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); + auto rc = m_ptr->m_input->reader_ext->read_entry_field(m_ptr->m_input->table, m_ptr->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()); + throw sinsp_exception(table_input_error_prefix(m_ptr->m_owner, m_ptr->m_input.get()) + "read field failure: " + m_ptr->m_owner->get_last_error()); } // note: strings are the only exception to the switch case below, @@ -472,10 +451,10 @@ struct plugin_table_wrapper: public libsinsp::state::table #undef _X } - auto rc = m_input->writer_ext->write_entry_field(m_input->table, m_entry, infos.m_accessors[i.index()], &v); + auto rc = m_ptr->m_input->writer_ext->write_entry_field(m_ptr->m_input->table, m_ptr->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()); + throw sinsp_exception(table_input_error_prefix(m_ptr->m_owner, m_ptr->m_input.get()) + "write field failure: " + m_ptr->m_owner->get_last_error()); } } private: @@ -483,7 +462,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { if (dynamic_fields() == nullptr) { - throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "local fields definitions not set"); + throw sinsp_exception(table_input_error_prefix(m_ptr->m_owner, m_ptr->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 @@ -560,7 +539,7 @@ struct plugin_table_wrapper: public libsinsp::state::table 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; + state->m_entry->m_ptr->m_entry = _e; __CATCH_ERR_MSG(state->err, { return (*state->m_it)(*state->m_entry) ? 1 : 0; }); @@ -576,12 +555,16 @@ struct plugin_table_wrapper: public libsinsp::state::table 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_ptr->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_ptr->m_entry = NULL; return true; } @@ -620,13 +603,13 @@ struct plugin_table_wrapper: public libsinsp::state::table 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); + auto res = m_input->writer_ext->add_table_entry(m_input->table, &keydata, entry->m_ptr->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; + entry->m_ptr->m_entry = res; + entry->m_ptr->m_detached = false; return std::move(e); } diff --git a/userspace/libsinsp/test/plugins/sample_table.h b/userspace/libsinsp/test/plugins/sample_table.h index 7035280b4d..37c85312fe 100644 --- a/userspace/libsinsp/test/plugins/sample_table.h +++ b/userspace/libsinsp/test/plugins/sample_table.h @@ -36,6 +36,12 @@ class sample_table public: virtual ~entry() { + // note: makes sure that release_table_entry is invoked consistently + if (refcount > 0) + { + fprintf(stderr, "sample_table: table entry deleted with non-zero refcount %ld\n", refcount); + exit(1); + } for (auto &p : data) { delete p; @@ -44,6 +50,7 @@ class sample_table private: std::vector data; std::vector strings; + uint64_t refcount; friend class sample_table; }; @@ -130,6 +137,7 @@ class sample_table auto it = t->entries.find(key->u64); if (it != t->entries.end()) { + it->second.refcount++; return static_cast(&it->second); } t->lasterr = "unknown entry at key: " + std::to_string(key->u64); @@ -159,7 +167,8 @@ class sample_table static void release_table_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) { - // there's no resource to release when an entry becomes unused + auto e = static_cast(_e); + e->refcount--; } 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) @@ -197,12 +206,15 @@ class sample_table static ss_plugin_table_entry_t *create_entry(ss_plugin_table_t *t) { - return static_cast(new sample_table::entry()); + auto e = new sample_table::entry(); + e->refcount = 1; + return static_cast(e); } static void destroy_entry(ss_plugin_table_t* _t, ss_plugin_table_entry_t* _e) { auto e = static_cast(_e); + e->refcount = 0; delete e; } @@ -210,9 +222,11 @@ class sample_table { auto t = static_cast(_t); auto e = static_cast(_e); + e->refcount = 0; t->entries.insert({ key->u64, *e }); delete e; - return &t->entries[key->u64]; + t->entries[key->u64].refcount = 1; + return static_cast(&t->entries[key->u64]); } 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) diff --git a/userspace/libsinsp/test/plugins/syscall_extract.cpp b/userspace/libsinsp/test/plugins/syscall_extract.cpp index b1c32282ce..a9d94fadfd 100644 --- a/userspace/libsinsp/test/plugins/syscall_extract.cpp +++ b/userspace/libsinsp/test/plugins/syscall_extract.cpp @@ -236,11 +236,13 @@ static ss_plugin_rc plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event_ { auto err = in->get_owner_last_error(in->owner); ps->lasterr = err ? err : ("can't read ope counter from thread with tid=" + std::to_string(ev->evt->tid)); + in->table_reader_ext->release_table_entry(ps->thread_table, thread); return SS_PLUGIN_FAILURE; } ps->u64storage = tmp.u64; in->fields[i].res.u64 = &ps->u64storage; in->fields[i].res_len = 1; + in->table_reader_ext->release_table_entry(ps->thread_table, thread); break; case 2: // evt_count if (!ps->evtcount_table || !ps->evtcount_count_field) @@ -282,11 +284,13 @@ static ss_plugin_rc plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event_ { auto err = in->get_owner_last_error(in->owner); ps->lasterr = err ? err : ("can't read event counter for type=" + std::to_string(ev->evt->type)); + in->table_reader_ext->release_table_entry(ps->evtcount_table, evtcount); return SS_PLUGIN_FAILURE; } ps->u64storage = tmp.u64; in->fields[i].res.u64 = &ps->u64storage; in->fields[i].res_len = 1; + in->table_reader_ext->release_table_entry(ps->evtcount_table, evtcount); break; case 3: // test.proc_name tmp.s64 = ev->evt->tid; @@ -302,12 +306,14 @@ static ss_plugin_rc plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event_ { auto err = in->get_owner_last_error(in->owner); ps->lasterr = err ? err : ("can't read proc name from thread with tid=" + std::to_string(ev->evt->tid)); + in->table_reader_ext->release_table_entry(ps->thread_table, thread); return SS_PLUGIN_FAILURE; } ps->strstorage = tmp.str; ps->strptrstorage = ps->strstorage.c_str(); in->fields[i].res.str = &ps->strptrstorage; in->fields[i].res_len = 1; + in->table_reader_ext->release_table_entry(ps->thread_table, thread); break; case 4: // sample.tick if (ev->evt->type == PPME_ASYNCEVENT_E diff --git a/userspace/libsinsp/test/plugins/syscall_parse.cpp b/userspace/libsinsp/test/plugins/syscall_parse.cpp index 27f3cdd3df..a9feff35f2 100644 --- a/userspace/libsinsp/test/plugins/syscall_parse.cpp +++ b/userspace/libsinsp/test/plugins/syscall_parse.cpp @@ -195,6 +195,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp ps->event_count_table->table, evtcounter, ps->event_count_table_count_field, &tmp)) { ps->lasterr = "can't read event counter in table"; + ps->event_count_table->reader_ext->release_table_entry(ps->event_count_table->table, evtcounter); return SS_PLUGIN_FAILURE; } tmp.u64++; @@ -202,8 +203,10 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp ps->event_count_table->table, evtcounter, ps->event_count_table_count_field, &tmp)) { ps->lasterr = "can't write event counter in table"; + ps->event_count_table->reader_ext->release_table_entry(ps->event_count_table->table, evtcounter); return SS_PLUGIN_FAILURE; } + ps->event_count_table->reader_ext->release_table_entry(ps->event_count_table->table, evtcounter); // update counter for current thread if (evt_type_is_open(ev->evt->type)) @@ -221,6 +224,7 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp { auto err = in->get_owner_last_error(in->owner); ps->lasterr = err ? err : ("can't read open counter from thread with tid=" + std::to_string(ev->evt->tid)); + in->table_reader_ext->release_table_entry(ps->thread_table, thread); return SS_PLUGIN_FAILURE; } @@ -230,8 +234,10 @@ static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_inp { auto err = in->get_owner_last_error(in->owner); ps->lasterr = err ? err : ("can't write open counter to thread with tid=" + std::to_string(ev->evt->tid)); + in->table_reader_ext->release_table_entry(ps->thread_table, thread); return SS_PLUGIN_FAILURE; } + in->table_reader_ext->release_table_entry(ps->thread_table, thread); } return SS_PLUGIN_SUCCESS; diff --git a/userspace/plugin/plugin_api.h b/userspace/plugin/plugin_api.h index 2f076a7dbd..65f0461f1f 100644 --- a/userspace/plugin/plugin_api.h +++ b/userspace/plugin/plugin_api.h @@ -181,7 +181,8 @@ typedef struct // with the given key. If another entry is already present with the same key, // it gets replaced. After insertion, table will be come the owner of the // entry's pointer. Returns an opaque pointer to the newly-added table's entry, - // or NULL in case of error. + // or NULL in case of error. Every non-NULL returned entry must be released + // by invoking release_table_entry() once it becomes no more used by the invoker. ss_plugin_table_entry_t* (*add_table_entry)(ss_plugin_table_t* t, const ss_plugin_state_data* key, ss_plugin_table_entry_t* entry); // // Updates a table's entry by writing a value for one of its fields. From e7094702912254c450757c05453776c5bce40f87 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 18:45:51 +0000 Subject: [PATCH 42/66] update(userspace): support post-init event code filters in plugins Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin.cpp | 38 +++++++++++++++++++++++++++++------ userspace/libsinsp/plugin.h | 12 +++-------- userspace/plugin/plugin_api.h | 36 ++++++++++++++++++++++++++------- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/userspace/libsinsp/plugin.cpp b/userspace/libsinsp/plugin.cpp index 0e522a21c3..79578f1b6b 100755 --- a/userspace/libsinsp/plugin.cpp +++ b/userspace/libsinsp/plugin.cpp @@ -192,6 +192,18 @@ bool sinsp_plugin::init(const std::string &config, std::string &errstr) return false; } + // resolve post-init event code filters + if (m_caps & CAP_EXTRACTION) + { + resolve_dylib_compatible_codes(m_handle->api.get_extract_event_types, + m_extract_event_sources, m_extract_event_codes); + } + if (m_caps & CAP_PARSING) + { + resolve_dylib_compatible_codes(m_handle->api.get_parse_event_types, + m_parse_event_sources, m_parse_event_codes); + } + return true; } @@ -291,7 +303,7 @@ void sinsp_plugin::resolve_dylib_field_arg(Json::Value root, filtercheck_field_i // this logic is shared between the field extraction and event parsing caps void sinsp_plugin::resolve_dylib_compatible_codes( - uint16_t *(*get_codes)(uint32_t *numtypes), + uint16_t *(*get_codes)(uint32_t*,ss_plugin_t*), const std::unordered_set& sources, libsinsp::events::set& codes) { @@ -299,7 +311,7 @@ void sinsp_plugin::resolve_dylib_compatible_codes( if (get_codes != NULL) { uint32_t ntypes = 0; - auto types = get_codes(&ntypes); + auto types = get_codes(&ntypes, m_state); if (types) { for (uint32_t i = 0; i < ntypes; i++) @@ -546,16 +558,12 @@ bool sinsp_plugin::resolve_dylib_symbols(std::string &errstr) resolve_dylib_compatible_sources("get_extract_event_sources", m_handle->api.get_extract_event_sources, m_extract_event_sources); - resolve_dylib_compatible_codes(m_handle->api.get_extract_event_types, - m_extract_event_sources, m_extract_event_codes); } if(m_caps & CAP_PARSING) { resolve_dylib_compatible_sources("get_parse_event_sources", m_handle->api.get_parse_event_sources, m_parse_event_sources); - resolve_dylib_compatible_codes(m_handle->api.get_parse_event_types, - m_parse_event_sources, m_parse_event_codes); } if(m_caps & CAP_ASYNC) @@ -579,6 +587,24 @@ std::string sinsp_plugin::get_init_schema(ss_plugin_schema_type& schema_type) co return std::string(""); } +const libsinsp::events::set& sinsp_plugin::extract_event_codes() const +{ + if (!m_inited) + { + throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name); + } + return m_extract_event_codes; +} + +const libsinsp::events::set& sinsp_plugin::parse_event_codes() const +{ + if (!m_inited) + { + throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name); + } + return m_parse_event_codes; +} + void sinsp_plugin::validate_init_config(std::string& config) { ss_plugin_schema_type schema_type; diff --git a/userspace/libsinsp/plugin.h b/userspace/libsinsp/plugin.h index 0d2c901ba5..70d14bd540 100755 --- a/userspace/libsinsp/plugin.h +++ b/userspace/libsinsp/plugin.h @@ -184,10 +184,7 @@ class sinsp_plugin return m_extract_event_sources; } - inline const libsinsp::events::set& extract_event_codes() const - { - return m_extract_event_codes; - } + const libsinsp::events::set& extract_event_codes() const; inline const std::vector& fields() const { @@ -202,10 +199,7 @@ class sinsp_plugin return m_parse_event_sources; } - inline const libsinsp::events::set& parse_event_codes() const - { - return m_parse_event_codes; - } + const libsinsp::events::set& parse_event_codes() const; bool parse_event(sinsp_evt* evt) const; @@ -278,7 +272,7 @@ class sinsp_plugin const char *(*get_sources)(), std::unordered_set& sources); void resolve_dylib_compatible_codes( - uint16_t *(*get_codes)(uint32_t *numtypes), + uint16_t *(*get_codes)(uint32_t* numtypes,ss_plugin_t* s), const std::unordered_set& sources, libsinsp::events::set& codes); void validate_init_config_json_schema(std::string& config, std::string& schema); diff --git a/userspace/plugin/plugin_api.h b/userspace/plugin/plugin_api.h index 65f0461f1f..48857b6314 100644 --- a/userspace/plugin/plugin_api.h +++ b/userspace/plugin/plugin_api.h @@ -27,8 +27,7 @@ extern "C" { // // API versions of this plugin framework // -// todo(jasondellaluce): when/if major changes to v4, solve all todos -// in this file related to deprecated types. +// todo(jasondellaluce): when/if major changes to v4, check and solve all todos #define PLUGIN_API_VERSION_MAJOR 3 #define PLUGIN_API_VERSION_MINOR 1 #define PLUGIN_API_VERSION_PATCH 0 @@ -670,8 +669,18 @@ typedef struct struct { // - // Return the list of event types that this plugin can consume + // Return the list of event types that this plugin will receive // for field extraction. The event types follow the libscap specific. + // This will be invoked only once by the framework after the plugin's + // initialization. Events that are not included in the returned list + // will not be received by the plugin. + // + // This is a non-functional filter that should not influence the plugin's + // functional behavior. Instead, this is a performance optimization + // with the goal of avoiding unnecessary communication between the + // framework and the plugin for events that are known to be not used for + // field extraction. + // // Required: no // // This function is optional--if NULL or an empty array, then: @@ -679,7 +688,9 @@ typedef struct // get_extract_event_sources (either default or custom) is compatible // with the "syscall" event source, otherwise // - the plugin will only receive events of plugin type (code 322). - uint16_t* (*get_extract_event_types)(uint32_t* numtypes); + // todo(jasondellaluce): when/if major changes to v4, reorder the arguments + // and put ss_plugin_t* as first + uint16_t* (*get_extract_event_types)(uint32_t* numtypes, ss_plugin_t* s); // // Return a string describing the event sources that this plugin @@ -757,8 +768,17 @@ typedef struct struct { // - // Return the list of event types that this plugin is capable of parsing. - // The event types follow the libscap specific. + // Return the list of event types that this plugin will receive + // for event parsing. The event types follow the libscap specific. + // This will be invoked only once by the framework after the plugin's + // initialization. Events that are not included in the returned list + // will not be received by the plugin. + // + // This is a non-functional filter that should not influence the plugin's + // functional behavior. Instead, this is a performance optimization + // with the goal of avoiding unnecessary communication between the + // framework and the plugin for events that are known to be not used for + // event parsing. // // Required: no // @@ -767,7 +787,9 @@ typedef struct // get_parse_event_sources (either default or custom) is compatible // with the "syscall" event source, otherwise // - the plugin will only receive events of plugin type (code 322). - uint16_t* (*get_parse_event_types)(uint32_t* numtypes); + // todo(jasondellaluce): when/if major changes to v4, reorder the arguments + // and put ss_plugin_t* as first + uint16_t* (*get_parse_event_types)(uint32_t* numtypes, ss_plugin_t* s); // // Return a string describing the event sources that this plugin // is capable of parsing. From deec389d02d8751ffd5fc12437bd77901b9e60c7 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 21 Jul 2023 18:46:20 +0000 Subject: [PATCH 43/66] test(userspace/libsinsp): update plugin tests Signed-off-by: Jason Dellaluce --- userspace/libsinsp/test/plugins.ut.cpp | 20 +++++++++- .../libsinsp/test/plugins/plugin_extract.cpp | 40 +++++++++++++++++++ .../libsinsp/test/plugins/syscall_extract.cpp | 2 +- .../libsinsp/test/plugins/syscall_parse.cpp | 2 +- userspace/libsinsp/test/plugins/tables.cpp | 2 +- 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/userspace/libsinsp/test/plugins.ut.cpp b/userspace/libsinsp/test/plugins.ut.cpp index fe2afb8fa7..06658e2da8 100644 --- a/userspace/libsinsp/test/plugins.ut.cpp +++ b/userspace/libsinsp/test/plugins.ut.cpp @@ -245,6 +245,7 @@ TEST_F(sinsp_with_test_input, plugin_custom_source) TEST(sinsp_plugin, plugin_extract_compatibility) { + std::string tmp; sinsp i; plugin_api api; get_plugin_api_sample_plugin_extract(api); @@ -252,6 +253,7 @@ TEST(sinsp_plugin, plugin_extract_compatibility) // compatible event sources specified, event types not specified api.get_name = [](){ return "p1"; }; auto p = i.register_plugin(&api); + p->init("", tmp); ASSERT_EQ(p->extract_event_sources().size(), 1); ASSERT_TRUE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), "sample")); ASSERT_FALSE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), sinsp_syscall_event_source_name)); @@ -259,14 +261,28 @@ TEST(sinsp_plugin, plugin_extract_compatibility) ASSERT_TRUE(p->extract_event_codes().contains(PPME_PLUGINEVENT_E)); ASSERT_FALSE(p->extract_event_codes().contains(PPME_SYSCALL_OPEN_E)); + // compatible event sources specified, event types specified (config-altered) + api.get_name = [](){ return "p1-2"; }; + p = i.register_plugin(&api); + ASSERT_ANY_THROW(p->extract_event_codes()); // can't be called before init + p->init("322,402", tmp); + ASSERT_EQ(p->extract_event_sources().size(), 1); + ASSERT_TRUE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), "sample")); + ASSERT_FALSE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), sinsp_syscall_event_source_name)); + ASSERT_EQ(p->extract_event_codes().size(), 2); + ASSERT_TRUE(p->extract_event_codes().contains(PPME_PLUGINEVENT_E)); + ASSERT_TRUE(p->extract_event_codes().contains(PPME_ASYNCEVENT_E)); + ASSERT_FALSE(p->extract_event_codes().contains(PPME_SYSCALL_OPEN_E)); + // compatible event sources specified, event types specified api.get_name = [](){ return "p2"; }; - api.get_extract_event_types = [](uint32_t* n) { + api.get_extract_event_types = [](uint32_t* n, ss_plugin_t* s) { static uint16_t ret[] = { PPME_SYSCALL_OPEN_E }; *n = sizeof(ret) / sizeof(uint16_t); return &ret[0]; }; p = i.register_plugin(&api); + p->init("", tmp); ASSERT_EQ(p->extract_event_sources().size(), 1); ASSERT_TRUE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), "sample")); ASSERT_FALSE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), sinsp_syscall_event_source_name)); @@ -279,6 +295,7 @@ TEST(sinsp_plugin, plugin_extract_compatibility) api.get_extract_event_sources = NULL; api.get_extract_event_types = NULL; p = i.register_plugin(&api); + p->init("", tmp); ASSERT_EQ(p->extract_event_sources().size(), 0); ASSERT_TRUE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), "sample")); ASSERT_TRUE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), sinsp_syscall_event_source_name)); @@ -296,6 +313,7 @@ TEST(sinsp_plugin, plugin_extract_compatibility) api.close = src_api.close; api.next_batch = src_api.next_batch; p = i.register_plugin(&api); + p->init("", tmp); ASSERT_EQ(p->extract_event_sources().size(), 1); ASSERT_TRUE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), "sample")); ASSERT_FALSE(sinsp_plugin::is_source_compatible(p->extract_event_sources(), sinsp_syscall_event_source_name)); diff --git a/userspace/libsinsp/test/plugins/plugin_extract.cpp b/userspace/libsinsp/test/plugins/plugin_extract.cpp index a21d6b8005..246350ed36 100644 --- a/userspace/libsinsp/test/plugins/plugin_extract.cpp +++ b/userspace/libsinsp/test/plugins/plugin_extract.cpp @@ -18,6 +18,7 @@ limitations under the License. #include #include #include +#include #include @@ -33,6 +34,7 @@ typedef struct plugin_state std::string lasterr; std::string strstorage; const char* strptr; + std::vector event_types; } plugin_state; static const char* plugin_get_required_api_version() @@ -73,9 +75,46 @@ static const char* plugin_get_extract_event_sources() return "[\"sample\"]"; } +static uint16_t* plugin_get_extract_event_types(uint32_t* num_types, ss_plugin_t* s) +{ + plugin_state *ps = (plugin_state *) s; + if (!ps->event_types.empty()) + { + *num_types = (uint32_t) ps->event_types.size(); + return ps->event_types.data(); + } + + static uint16_t types[] = {}; + *num_types = 0; + return types; +} + static ss_plugin_t* plugin_init(const ss_plugin_init_input* in, ss_plugin_rc* rc) { plugin_state *ret = new plugin_state(); + + // init config may indicate the comma-separated, event-types to filter + std::string cfg = in->config; + if (!cfg.empty()) + { + if (cfg.back() != ',') + { + cfg += ","; + } + std::string val; + std::stringstream test(cfg); + while(std::getline(test, val, ',')) + { + auto v = std::atoi(val.c_str()); + if (v == 0) + { + ret->lasterr = "invalid init config string: " + cfg; + return ret; + } + ret->event_types.push_back((uint16_t) v); + } + } + *rc = SS_PLUGIN_SUCCESS; return ret; } @@ -124,5 +163,6 @@ void get_plugin_api_sample_plugin_extract(plugin_api& out) out.destroy = plugin_destroy; out.get_fields = plugin_get_fields; out.get_extract_event_sources = plugin_get_extract_event_sources; + out.get_extract_event_types = plugin_get_extract_event_types; out.extract_fields = plugin_extract_fields; } diff --git a/userspace/libsinsp/test/plugins/syscall_extract.cpp b/userspace/libsinsp/test/plugins/syscall_extract.cpp index a9d94fadfd..4a3e3a3e4a 100644 --- a/userspace/libsinsp/test/plugins/syscall_extract.cpp +++ b/userspace/libsinsp/test/plugins/syscall_extract.cpp @@ -106,7 +106,7 @@ static const char* plugin_get_extract_event_sources() return "[\"syscall\"]"; } -static uint16_t* plugin_get_extract_event_types(uint32_t* num_types) +static uint16_t* plugin_get_extract_event_types(uint32_t* num_types, ss_plugin_t* s) { static uint16_t types[] = { PPME_SYSCALL_OPEN_E, diff --git a/userspace/libsinsp/test/plugins/syscall_parse.cpp b/userspace/libsinsp/test/plugins/syscall_parse.cpp index a9feff35f2..fc8956e4cc 100644 --- a/userspace/libsinsp/test/plugins/syscall_parse.cpp +++ b/userspace/libsinsp/test/plugins/syscall_parse.cpp @@ -86,7 +86,7 @@ static const char* plugin_get_parse_event_sources() return "[\"syscall\"]"; } -static uint16_t* plugin_get_parse_event_types(uint32_t* num_types) +static uint16_t* plugin_get_parse_event_types(uint32_t* num_types, ss_plugin_t* s) { static uint16_t types[] = { PPME_SYSCALL_OPEN_E, diff --git a/userspace/libsinsp/test/plugins/tables.cpp b/userspace/libsinsp/test/plugins/tables.cpp index 2965bf35c5..f5cdf437b0 100644 --- a/userspace/libsinsp/test/plugins/tables.cpp +++ b/userspace/libsinsp/test/plugins/tables.cpp @@ -68,7 +68,7 @@ static const char* plugin_get_parse_event_sources() return "[\"syscall\"]"; } -static uint16_t* plugin_get_parse_event_types(uint32_t* num_types) +static uint16_t* plugin_get_parse_event_types(uint32_t* num_types, ss_plugin_t* s) { static uint16_t types[] = {}; *num_types = 0; From fbfc59618138b92b0e7565b5683c24907fc2ebd3 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Mon, 24 Jul 2023 16:05:36 +0000 Subject: [PATCH 44/66] fix(userspace/libsinsp): bad cast in plugins table api Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index afa676f53b..f17c02bf35 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -1177,12 +1177,12 @@ ss_plugin_rc sinsp_table_wrapper::read_entry_field(ss_plugin_table_t* _t, ss_plu if (a->dynamic) \ { \ auto aa = static_cast*>(a->accessor); \ - e->get_dynamic_field(*aa, out->_dtype); \ + e->get_dynamic_field<_type>(*aa, out->_dtype); \ } \ else \ { \ auto aa = static_cast*>(a->accessor); \ - e->get_static_field(*aa, out->_dtype); \ + e->get_static_field<_type>(*aa, out->_dtype); \ } \ return SS_PLUGIN_SUCCESS; \ } @@ -1215,7 +1215,7 @@ ss_plugin_rc sinsp_table_wrapper::write_entry_field(ss_plugin_table_t* _t, ss_pl if (a->dynamic) \ { \ auto aa = static_cast*>(a->accessor); \ - e->set_dynamic_field<_type>(*aa, reinterpret_cast(in->_dtype)); \ + e->set_dynamic_field<_type>(*aa, in->_dtype); \ } \ else \ { \ From 2321aaf686cd62588f9824d34c951616969070e0 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Mon, 24 Jul 2023 16:44:52 +0000 Subject: [PATCH 45/66] fix(userspace/libsinsp): simplify allocation flow in sinsp table api Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 102 +++++++++++------------- 1 file changed, 48 insertions(+), 54 deletions(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index f17c02bf35..1651fb6abb 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -331,43 +331,7 @@ struct plugin_table_wrapper: public libsinsp::state::table }; struct plugin_table_entry: public libsinsp::state::table_entry - { - struct ptr_wrap - { - ptr_wrap( - sinsp_plugin* o, - const owned_table_input_t& i, - ss_plugin_table_entry_t* e, - bool detached): - m_owner(o), - m_input(i), - m_entry(e), - m_detached(detached) {}; - ptr_wrap(ptr_wrap&&) = default; - ptr_wrap& operator = (ptr_wrap&&) = default; - ptr_wrap(const ptr_wrap& s) = default; - ptr_wrap& operator = (const ptr_wrap& s) = default; - virtual ~ptr_wrap() - { - 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; - }; - + { plugin_table_entry( sinsp_plugin* o, const owned_table_input_t& i, @@ -375,14 +339,33 @@ struct plugin_table_wrapper: public libsinsp::state::table ss_plugin_table_entry_t* e, bool detached): table_entry(fields), - m_ptr(std::make_shared(o, i, e, detached)) {}; - plugin_table_entry(const plugin_table_entry& o) = default; - plugin_table_entry& operator = (const plugin_table_entry& o) = default; + 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() = 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); + } + } + } - std::shared_ptr m_ptr; + 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 @@ -392,7 +375,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { if (defs && dynamic_cast(defs.get()) == nullptr) { - throw sinsp_exception(table_input_error_prefix(m_ptr->m_owner, m_ptr->m_input.get()) + "plugin table can only be set with plugin dynamic fields"); + 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); } @@ -401,10 +384,10 @@ struct plugin_table_wrapper: public libsinsp::state::table { const auto& infos = get_plugin_field_infos(); ss_plugin_state_data dout; - auto rc = m_ptr->m_input->reader_ext->read_entry_field(m_ptr->m_input->table, m_ptr->m_entry, infos.m_accessors[i.index()], &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_ptr->m_owner, m_ptr->m_input.get()) + "read field failure: " + m_ptr->m_owner->get_last_error()); + 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, @@ -451,10 +434,10 @@ struct plugin_table_wrapper: public libsinsp::state::table #undef _X } - auto rc = m_ptr->m_input->writer_ext->write_entry_field(m_ptr->m_input->table, m_ptr->m_entry, infos.m_accessors[i.index()], &v); + 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_ptr->m_owner, m_ptr->m_input.get()) + "write field failure: " + m_ptr->m_owner->get_last_error()); + throw sinsp_exception(table_input_error_prefix(m_owner, m_input.get()) + "write field failure: " + m_owner->get_last_error()); } } private: @@ -462,7 +445,7 @@ struct plugin_table_wrapper: public libsinsp::state::table { if (dynamic_fields() == nullptr) { - throw sinsp_exception(table_input_error_prefix(m_ptr->m_owner, m_ptr->m_input.get()) + "local fields definitions not set"); + 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 @@ -539,7 +522,7 @@ struct plugin_table_wrapper: public libsinsp::state::table 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_ptr->m_entry = _e; + state->m_entry->m_entry = _e; __CATCH_ERR_MSG(state->err, { return (*state->m_it)(*state->m_entry) ? 1 : 0; }); @@ -556,7 +539,7 @@ struct plugin_table_wrapper: public libsinsp::state::table if (m_input->reader_ext->iterate_entries(m_input->table, table_iterator_func, s) == 0) { // avoids invoking release_table_entry - entry.m_ptr->m_entry = NULL; + 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); @@ -564,7 +547,7 @@ struct plugin_table_wrapper: public libsinsp::state::table return false; } // avoids invoking release_table_entry - entry.m_ptr->m_entry = NULL; + entry.m_entry = NULL; return true; } @@ -594,22 +577,28 @@ struct plugin_table_wrapper: public libsinsp::state::table 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_ptr->m_entry); + 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_ptr->m_entry = res; - entry->m_ptr->m_detached = false; + entry->m_entry = res; + entry->m_detached = false; return std::move(e); } @@ -952,6 +941,11 @@ struct sinsp_table_wrapper 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); \ From edc309259161e0fccb22a923ce739ba7c3c2b3d0 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Tue, 25 Jul 2023 11:40:53 +0200 Subject: [PATCH 46/66] update(ci): bumped kernel_tests to kernel-testing v0.2.0. Signed-off-by: Federico Di Pierro --- .github/workflows/kernel_tests.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index c41b7e4018..582cad2527 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -27,9 +27,11 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - repository: alacuku/e2e-falco-tests + repository: alacuku/kernel-testing + ref: v0.2.0 - name: Generate vars yaml + working-directory: ./ansible-playbooks run: | LIBS_V=${{ github.event.inputs.libsversion }} LIBS_VERSION=${LIBS_V:-"master"} @@ -41,18 +43,22 @@ jobs: EOF - name: Bootstrap VMs + working-directory: ./ansible-playbooks run: | ansible-playbook bootstrap.yml --extra-vars "@vars.yml" - name: Common setup + working-directory: ./ansible-playbooks run: | ansible-playbook common.yml --extra-vars "@vars.yml" - name: Prepare github repos + working-directory: ./ansible-playbooks run: | ansible-playbook git-repos.yml --extra-vars "@vars.yml" - name: Run scap-open tests + working-directory: ./ansible-playbooks run: | ansible-playbook scap-open.yml --extra-vars "@vars.yml" || : @@ -85,5 +91,6 @@ jobs: - name: Cleanup if: always() + working-directory: ./ansible-playbooks run: | ansible-playbook clean-up.yml --extra-vars "@vars.yml" || : From db3e089e2adaa8ab99dd3126477549da4d14c9ed Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Tue, 25 Jul 2023 13:50:59 +0200 Subject: [PATCH 47/66] chore(ci): switch kernel_tests repo to falcosecurity org. Signed-off-by: Federico Di Pierro --- .github/workflows/kernel_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index 582cad2527..d479f263dc 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -27,7 +27,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 with: - repository: alacuku/kernel-testing + repository: falcosecurity/kernel-testing ref: v0.2.0 - name: Generate vars yaml From d9dbca29d41cafde7e5e01c3a75855503c189a91 Mon Sep 17 00:00:00 2001 From: Gerald Combs Date: Fri, 21 Jul 2023 15:05:10 -0700 Subject: [PATCH 48/66] update(cmake): Make sure libscap.pc matches our install behavior Use the same LIBSCAP_INSTALL_LIBS for our install targets and as the starting point for our pkgconfig linker flags. Signed-off-by: Gerald Combs --- cmake/modules/libscap.cmake | 45 +++++++++++++++++++++++++------- userspace/libscap/CMakeLists.txt | 22 ---------------- userspace/libscap/libscap.pc.in | 2 +- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/cmake/modules/libscap.cmake b/cmake/modules/libscap.cmake index a87dcd5b0e..9792436421 100644 --- a/cmake/modules/libscap.cmake +++ b/cmake/modules/libscap.cmake @@ -53,15 +53,11 @@ endfunction() add_subdirectory(${LIBSCAP_DIR}/userspace/libscap ${PROJECT_BINARY_DIR}/libscap) -# We can switch to using the MANUALLY_ADDED_DEPENDENCIES when our minimum -# CMake version is 3.8 or later. -set(LIBSCAP_LIBS - scap - scap_engine_noop - scap_engine_source_plugin - scap_error - scap_event_schema) +set(LIBSCAP_INSTALL_LIBS scap) +# "Conditional" means it might not exist or it might be static. +# We might be able to automate this using the proposed ALL_BUILDSYSTEM_TARGETS: +# https://gitlab.kitware.com/cmake/cmake/-/issues/20124 set(libscap_conditional_libs driver_event_schema pman @@ -70,20 +66,49 @@ set(libscap_conditional_libs scap_engine_kmod scap_engine_modern_bpf scap_engine_nodriver + scap_engine_noop scap_engine_savefile + scap_engine_source_plugin scap_engine_test_input scap_engine_udig scap_engine_util + scap_error + scap_event_schema scap_platform scap_platform_util) +# Installation targets foreach(libscap_conditional_lib ${libscap_conditional_libs}) if(TARGET ${libscap_conditional_lib}) - list(APPEND LIBSCAP_LIBS ${libscap_conditional_lib}) + get_target_property(cl_target_type ${libscap_conditional_lib} TYPE) + if (NOT ${BUILD_SHARED_LIBS} OR ${cl_target_type} STREQUAL SHARED_LIBRARY) + list(APPEND LIBSCAP_INSTALL_LIBS ${libscap_conditional_lib}) + endif() endif() endforeach() -install(TARGETS ${LIBSCAP_LIBS} +# Installation targets and their dependencies +set(libscap_link_libraries) +foreach(libscap_install_lib ${LIBSCAP_INSTALL_LIBS}) + list(APPEND libscap_link_libraries ${libscap_install_lib}) + get_target_property(install_lib_link_libraries ${libscap_install_lib} LINK_LIBRARIES) + foreach (install_lib_link_library ${install_lib_link_libraries}) + if (NOT ${install_lib_link_library} IN_LIST libscap_conditional_libs) + list(APPEND libscap_link_libraries ${install_lib_link_libraries}) + endif() + endforeach() +endforeach() +list(REMOVE_DUPLICATES libscap_link_libraries) + +set(libscap_link_flags "") +foreach(libscap_link_library ${libscap_link_libraries}) + list(APPEND libscap_link_flags "-l${libscap_link_library}") +endforeach() + +string(REPLACE ";" " " LIBSCAP_LINK_LIBRARIES_FLAGS "${libscap_link_flags}") +configure_file(${LIBSCAP_DIR}/userspace/libscap/libscap.pc.in ${PROJECT_BINARY_DIR}/libscap/libscap.pc @ONLY) + +install(TARGETS ${LIBSCAP_INSTALL_LIBS} ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/${LIBS_PACKAGE_NAME}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" diff --git a/userspace/libscap/CMakeLists.txt b/userspace/libscap/CMakeLists.txt index 47699d4987..39253e544e 100644 --- a/userspace/libscap/CMakeLists.txt +++ b/userspace/libscap/CMakeLists.txt @@ -227,28 +227,6 @@ if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND CMAKE_SYSTEM_NAME MATCHES "Linux endif() endif() -# We might need to filter this. -get_target_property(scap_link_libraries scap LINK_LIBRARIES) -if(TARGET scap_engine_bpf) - get_target_property(scap_engine_bpf_link_libraries scap_engine_bpf LINK_LIBRARIES) - if (scap_engine_bpf_link_libraries) - list(APPEND scap_link_libraries ${scap_engine_bpf_link_libraries}) - endif() -endif() -#get_target_property(scap_engine_kmod_link_libraries scap_engine_kmod LINK_LIBRARIES) -get_target_property(scap_event_schema_link_libraries scap_event_schema LINK_LIBRARIES) -if (scap_event_schema_link_libraries) - list(APPEND scap_link_libraries ${scap_event_schema_link_libraries}) -endif() - -set(scap_link_flags "") -foreach(link_library scap_engine_noop ${scap_link_libraries}) - list(APPEND scap_link_flags "-l${link_library}") -endforeach() -list(REMOVE_DUPLICATES scap_link_flags) -string(REPLACE ";" " " SCAP_LINK_LIBRARIES_FLAGS "${scap_link_flags}") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libscap.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libscap.pc @ONLY) - if (BUILD_LIBSCAP_EXAMPLES) add_subdirectory(examples/01-open) add_subdirectory(examples/02-validatebuffer) diff --git a/userspace/libscap/libscap.pc.in b/userspace/libscap/libscap.pc.in index de3cf476c6..f05740e1ef 100644 --- a/userspace/libscap/libscap.pc.in +++ b/userspace/libscap/libscap.pc.in @@ -6,5 +6,5 @@ Name: libscap Description: lib for System CAPture Version: @FALCOSECURITY_LIBS_VERSION@ -Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ -lscap @SCAP_LINK_LIBRARIES_FLAGS@ +Libs: -L${libdir}/@LIBS_PACKAGE_NAME@ @LIBSCAP_LINK_LIBRARIES_FLAGS@ Cflags: -I${includedir}/@LIBS_PACKAGE_NAME@/userspace/libscap From 1bdc44e444c66b002d7b442b5163f4fb8ec82d69 Mon Sep 17 00:00:00 2001 From: Gerald Combs Date: Sat, 22 Jul 2023 12:52:20 -0700 Subject: [PATCH 49/66] update(cmake): Fix our libscap dependency list Add a couple of minor fixups as well. Signed-off-by: Gerald Combs --- cmake/modules/libscap.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/modules/libscap.cmake b/cmake/modules/libscap.cmake index 9792436421..a019d83822 100644 --- a/cmake/modules/libscap.cmake +++ b/cmake/modules/libscap.cmake @@ -55,7 +55,7 @@ add_subdirectory(${LIBSCAP_DIR}/userspace/libscap ${PROJECT_BINARY_DIR}/libscap) set(LIBSCAP_INSTALL_LIBS scap) -# "Conditional" means it might not exist or it might be static. +# "Conditional" means the target might not exist or it might be static. # We might be able to automate this using the proposed ALL_BUILDSYSTEM_TARGETS: # https://gitlab.kitware.com/cmake/cmake/-/issues/20124 set(libscap_conditional_libs @@ -94,13 +94,13 @@ foreach(libscap_install_lib ${LIBSCAP_INSTALL_LIBS}) get_target_property(install_lib_link_libraries ${libscap_install_lib} LINK_LIBRARIES) foreach (install_lib_link_library ${install_lib_link_libraries}) if (NOT ${install_lib_link_library} IN_LIST libscap_conditional_libs) - list(APPEND libscap_link_libraries ${install_lib_link_libraries}) + list(APPEND libscap_link_libraries ${install_lib_link_library}) endif() endforeach() endforeach() list(REMOVE_DUPLICATES libscap_link_libraries) -set(libscap_link_flags "") +set(libscap_link_flags) foreach(libscap_link_library ${libscap_link_libraries}) list(APPEND libscap_link_flags "-l${libscap_link_library}") endforeach() From 5823fff5f6453de6aacdb4a9c25a6437e5168187 Mon Sep 17 00:00:00 2001 From: Gerald Combs Date: Sat, 22 Jul 2023 14:20:17 -0700 Subject: [PATCH 50/66] update(cmake): Automatically build our libscap library list Use the SUBDIRECTORIES and BUILDSYSTEM_TARGETS properties to automatically build our libscap library list. Signed-off-by: Gerald Combs --- cmake/modules/libscap.cmake | 54 +++++++++++++++---------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/cmake/modules/libscap.cmake b/cmake/modules/libscap.cmake index a019d83822..2a73fe6075 100644 --- a/cmake/modules/libscap.cmake +++ b/cmake/modules/libscap.cmake @@ -53,37 +53,27 @@ endfunction() add_subdirectory(${LIBSCAP_DIR}/userspace/libscap ${PROJECT_BINARY_DIR}/libscap) -set(LIBSCAP_INSTALL_LIBS scap) - -# "Conditional" means the target might not exist or it might be static. -# We might be able to automate this using the proposed ALL_BUILDSYSTEM_TARGETS: -# https://gitlab.kitware.com/cmake/cmake/-/issues/20124 -set(libscap_conditional_libs - driver_event_schema - pman - scap_engine_bpf - scap_engine_gvisor - scap_engine_kmod - scap_engine_modern_bpf - scap_engine_nodriver - scap_engine_noop - scap_engine_savefile - scap_engine_source_plugin - scap_engine_test_input - scap_engine_udig - scap_engine_util - scap_error - scap_event_schema - scap_platform - scap_platform_util) - -# Installation targets -foreach(libscap_conditional_lib ${libscap_conditional_libs}) - if(TARGET ${libscap_conditional_lib}) - get_target_property(cl_target_type ${libscap_conditional_lib} TYPE) - if (NOT ${BUILD_SHARED_LIBS} OR ${cl_target_type} STREQUAL SHARED_LIBRARY) - list(APPEND LIBSCAP_INSTALL_LIBS ${libscap_conditional_lib}) - endif() +set(LIBSCAP_INSTALL_LIBS) + +# All of the targets in userspace/libscap +get_directory_property(libscap_subdirs DIRECTORY ${LIBSCAP_DIR}/userspace/libscap SUBDIRECTORIES) +list(PREPEND libscap_subdirs ${LIBSCAP_DIR}/userspace/libscap) +set(libscap_subdir_targets) +foreach(libscap_subdir ${libscap_subdirs}) + get_directory_property(subdir_targets DIRECTORY ${libscap_subdir} BUILDSYSTEM_TARGETS) + list(APPEND libscap_subdir_targets ${subdir_targets}) +endforeach() + +set(install_lib_type STATIC_LIBRARY) +if (BUILD_SHARED_LIBS) + set(install_lib_type SHARED_LIBRARY) +endif() + +# Installation targets only +foreach(libscap_subdir_target ${libscap_subdir_targets}) + get_target_property(cl_target_type ${libscap_subdir_target} TYPE) + if (${cl_target_type} STREQUAL ${install_lib_type}) + list(APPEND LIBSCAP_INSTALL_LIBS ${libscap_subdir_target}) endif() endforeach() @@ -93,7 +83,7 @@ foreach(libscap_install_lib ${LIBSCAP_INSTALL_LIBS}) list(APPEND libscap_link_libraries ${libscap_install_lib}) get_target_property(install_lib_link_libraries ${libscap_install_lib} LINK_LIBRARIES) foreach (install_lib_link_library ${install_lib_link_libraries}) - if (NOT ${install_lib_link_library} IN_LIST libscap_conditional_libs) + if (NOT ${install_lib_link_library} IN_LIST libscap_subdir_targets) list(APPEND libscap_link_libraries ${install_lib_link_library}) endif() endforeach() From ac1eec01b8c15f878a54762bde594ddec37bfee8 Mon Sep 17 00:00:00 2001 From: Gerald Combs Date: Sun, 23 Jul 2023 12:10:04 -0700 Subject: [PATCH 51/66] fix(cmake): list PREPEND was added in CMake 3.15 Signed-off-by: Gerald Combs --- cmake/modules/libscap.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/modules/libscap.cmake b/cmake/modules/libscap.cmake index 2a73fe6075..ca2207cca2 100644 --- a/cmake/modules/libscap.cmake +++ b/cmake/modules/libscap.cmake @@ -57,9 +57,8 @@ set(LIBSCAP_INSTALL_LIBS) # All of the targets in userspace/libscap get_directory_property(libscap_subdirs DIRECTORY ${LIBSCAP_DIR}/userspace/libscap SUBDIRECTORIES) -list(PREPEND libscap_subdirs ${LIBSCAP_DIR}/userspace/libscap) set(libscap_subdir_targets) -foreach(libscap_subdir ${libscap_subdirs}) +foreach(libscap_subdir ${LIBSCAP_DIR}/userspace/libscap ${libscap_subdirs}) get_directory_property(subdir_targets DIRECTORY ${libscap_subdir} BUILDSYSTEM_TARGETS) list(APPEND libscap_subdir_targets ${subdir_targets}) endforeach() From eff812575d1261effad748ad777c914a57c901a1 Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Tue, 25 Jul 2023 15:59:25 +0200 Subject: [PATCH 52/66] fix(scap): fix platform initalization order (again) While we need the proclist callback to be initialized before the engine (which calls for initializing the platform first), we also call linux_vtable methods (get_vtid) during the /proc scan (which happens inside linux platform_init). That implies initializing the engine first. Given this situation, we can only split the platform setup into two parts: - initializing the generic part (which engines may depend on) - initializing the platform specific part (on which no engine should ever depend) With the engine initialization in between the two. 656be921a5a6ab7cacb1cd4b370c1593a2c2a02f was a previous attempt to fix the initialization order and this commit (mostly) reverts it. Signed-off-by: Grzegorz Nosek --- userspace/libscap/scap.c | 7 ++++++- userspace/libscap/scap_platform.c | 9 +-------- userspace/libscap/scap_platform.h | 9 +++++++-- userspace/libscap/scap_platform_impl.h | 4 +--- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/userspace/libscap/scap.c b/userspace/libscap/scap.c index 51fe07c506..0a3012cbd2 100644 --- a/userspace/libscap/scap.c +++ b/userspace/libscap/scap.c @@ -63,7 +63,7 @@ int32_t scap_init_int(scap_t* handle, scap_open_args* oargs, const struct scap_v return SCAP_FAILURE; } - if((rc = scap_platform_init(handle->m_platform, handle->m_lasterr, handle->m_engine, oargs)) != SCAP_SUCCESS) + if((rc = scap_generic_init_platform(handle->m_platform, handle->m_lasterr, oargs)) != SCAP_SUCCESS) { return rc; } @@ -75,6 +75,11 @@ int32_t scap_init_int(scap_t* handle, scap_open_args* oargs, const struct scap_v return rc; } + if((rc = scap_platform_init(handle->m_platform, handle->m_lasterr, handle->m_engine, oargs)) != SCAP_SUCCESS) + { + return rc; + } + rc = check_api_compatibility(handle->m_vtable, handle->m_engine, handle->m_lasterr); if(rc != SCAP_SUCCESS) { diff --git a/userspace/libscap/scap_platform.c b/userspace/libscap/scap_platform.c index f0cc6b5568..571336270f 100644 --- a/userspace/libscap/scap_platform.c +++ b/userspace/libscap/scap_platform.c @@ -23,7 +23,7 @@ limitations under the License. #include "scap-int.h" #include "scap_os_machine_info.h" -static int32_t scap_generic_init_platform(struct scap_platform* platform, char* lasterr, struct scap_open_args* oargs) +int32_t scap_generic_init_platform(struct scap_platform* platform, char* lasterr, struct scap_open_args* oargs) { memset(&platform->m_machine_info, 0, sizeof(platform->m_machine_info)); if(scap_os_get_machine_info(&platform->m_machine_info, lasterr) != SCAP_SUCCESS) @@ -99,13 +99,6 @@ int32_t scap_platform_init(struct scap_platform *platform, char *lasterr, struct return SCAP_SUCCESS; } - rc = scap_generic_init_platform(platform, lasterr, oargs); - if(rc != SCAP_SUCCESS) - { - scap_platform_close(platform); - return rc; - } - if(platform->m_vtable && platform->m_vtable->init_platform) { rc = platform->m_vtable->init_platform(platform, lasterr, engine, oargs); diff --git a/userspace/libscap/scap_platform.h b/userspace/libscap/scap_platform.h index c6a0d162d5..4947ee1c65 100644 --- a/userspace/libscap/scap_platform.h +++ b/userspace/libscap/scap_platform.h @@ -37,9 +37,14 @@ struct scap_platform; // allocate a generic platform handle with no behavior (no platform data is returned) struct scap_platform* scap_generic_alloc_platform(); +// initialize the common part of the platform handle +// this needs to be called before opening the engine +// as otherwise the proclist callback won't be set up in time +// (for the savefile engine) +int32_t scap_generic_init_platform(struct scap_platform* platform, char* lasterr, struct scap_open_args* oargs); + // initialize a platform handle -// this calls `init_platform` from the vtable and also -// does any common initialization +// this calls `init_platform` from the vtable int32_t scap_platform_init(struct scap_platform *platform, char *lasterr, struct scap_engine_handle engine, struct scap_open_args *oargs); diff --git a/userspace/libscap/scap_platform_impl.h b/userspace/libscap/scap_platform_impl.h index c93629dde0..8ebfd70edc 100644 --- a/userspace/libscap/scap_platform_impl.h +++ b/userspace/libscap/scap_platform_impl.h @@ -47,9 +47,7 @@ struct ppm_proclist_info; struct scap_platform_vtable { // initialize the platform-specific structure - // at this point the engine is allocated but *not initialized yet* - // so it can only be stored away for future use - // (the engine will be initialized before any other platform method is called) + // at this point the engine is fully initialized and operational int32_t (*init_platform)(struct scap_platform* platform, char* lasterr, struct scap_engine_handle engine, struct scap_open_args* oargs); // refresh the interface list and place it inside From afd89033f7e90ceb866c762e028675aae0b99cec Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Tue, 25 Jul 2023 16:27:25 +0200 Subject: [PATCH 53/66] update(ci): bumped kernel-testing to v0.2.1. Signed-off-by: Federico Di Pierro --- .github/workflows/kernel_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index d479f263dc..abf4f0c098 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v3 with: repository: falcosecurity/kernel-testing - ref: v0.2.0 + ref: v0.2.1 - name: Generate vars yaml working-directory: ./ansible-playbooks From b1702a733a2b8050460361a17645c853d94e6c9d Mon Sep 17 00:00:00 2001 From: Aldo Lacuku Date: Wed, 26 Jul 2023 09:20:34 +0200 Subject: [PATCH 54/66] update(ci): bump kernel-testing to v.2.2 Signed-off-by: Aldo Lacuku --- .github/workflows/kernel_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index abf4f0c098..e3bb257937 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v3 with: repository: falcosecurity/kernel-testing - ref: v0.2.1 + ref: v0.2.2 - name: Generate vars yaml working-directory: ./ansible-playbooks From c5debc1d2a7b8c8cbe87566798cbcd5841c4f38f Mon Sep 17 00:00:00 2001 From: Grzegorz Nosek Date: Wed, 26 Jul 2023 15:23:24 +0200 Subject: [PATCH 55/66] fix(modern_bpf): fix NULL dereference in signal_deliver filler The `signal_deliver` filler can be called with info=NULL (`SEND_SIG_NOINFO`). Despite all I've been led to believe with eBPF, this does cause an actual NULL dereference in the kernel, promptly killing the machine (as the offending thread dies while holding the spinlock in get_signal). So let's check the pointer before we dereference it. Signed-off-by: Grzegorz Nosek Co-Authored-By: Andrea Terzolo --- .../attached/events/signal_deliver.bpf.c | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/driver/modern_bpf/programs/attached/events/signal_deliver.bpf.c b/driver/modern_bpf/programs/attached/events/signal_deliver.bpf.c index 1b7da84dbd..3c28415194 100644 --- a/driver/modern_bpf/programs/attached/events/signal_deliver.bpf.c +++ b/driver/modern_bpf/programs/attached/events/signal_deliver.bpf.c @@ -33,43 +33,46 @@ int BPF_PROG(signal_deliver, /* Try to find the source pid */ pid_t spid = 0; - switch(sig) + if(info != NULL) { - case SIGKILL: - spid = info->_sifields._kill._pid; - break; - - case SIGTERM: - case SIGHUP: - case SIGINT: - case SIGTSTP: - case SIGQUIT: - { - int si_code = info->si_code; - if(si_code == SI_USER || - si_code == SI_QUEUE || - si_code <= 0) + switch(sig) { - /* This is equivalent to `info->si_pid` where - * `si_pid` is a macro `_sifields._kill._pid` - */ + case SIGKILL: spid = info->_sifields._kill._pid; + break; + + case SIGTERM: + case SIGHUP: + case SIGINT: + case SIGTSTP: + case SIGQUIT: + { + int si_code = info->si_code; + if(si_code == SI_USER || + si_code == SI_QUEUE || + si_code <= 0) + { + /* This is equivalent to `info->si_pid` where + * `si_pid` is a macro `_sifields._kill._pid` + */ + spid = info->_sifields._kill._pid; + } + break; } - break; - } - case SIGCHLD: - spid = info->_sifields._sigchld._pid; - break; + case SIGCHLD: + spid = info->_sifields._sigchld._pid; + break; - default: - spid = 0; - break; - } + default: + spid = 0; + break; + } - if(sig >= SIGRTMIN && sig <= SIGRTMAX) - { - spid = info->_sifields._rt._pid; + if(sig >= SIGRTMIN && sig <= SIGRTMAX) + { + spid = info->_sifields._rt._pid; + } } /* Parameter 1: spid (type: PT_PID) */ From 94619b473f7dcbd76d3c7d0e239d52eb75d8dee9 Mon Sep 17 00:00:00 2001 From: Luca Guerra Date: Wed, 26 Jul 2023 15:39:50 +0000 Subject: [PATCH 56/66] update(readme): update readme, link to the falco website Signed-off-by: Luca Guerra --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd2c5286b0..9c27c14012 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ [![Kernel Tests](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml/badge.svg)](https://github.com/falcosecurity/libs/actions/workflows/kernel_tests.yaml) [![Github Pages](https://github.com/falcosecurity/libs/actions/workflows/pages.yml/badge.svg)](https://falcosecurity.github.io/libs/) -As per the [OSS Libraries Contribution Plan](https://github.com/falcosecurity/falco/blob/master/proposals/20210119-libraries-contribution.md), this repository has been chosen to be the new home for **libsinsp**, **libscap**, the **kernel module** and the **eBPF probe** sources. -Refer to https://falco.org/blog/contribution-drivers-kmod-ebpf-libraries/ for more information. +This repository contains **libsinsp**, **libscap**, the **kernel module** and the **eBPF probes** sources. These components are at the foundation of [Falco](https://github.com/falcosecurity/falco) and other projects that work with the same kind of data. @@ -42,6 +41,8 @@ plus chisels related code and common utilities. external dependencies, plus the libscap and libsinsp ones; consumers (like Falco) use those modules to build the libs in their projects. + For an overview of the event sources that are implemented by the libs see also the official [Falco documentation](https://falco.org/docs/event-sources/). + ## Versioning This project uses two different versioning schemes for the _libs_ and _driver_ components. In particular, the _driver_ versions are suffixed with `+driver` to distinguish them from the _libs_ ones. Both adhere to the [Semantic Versioning 2.0.0](https://semver.org/). You can find more detail about how we version those components in our [release process documentation](./release.md). From 646bd0cbd22c8632812c174751f191c4e4b92933 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Thu, 27 Jul 2023 09:56:14 +0200 Subject: [PATCH 57/66] new(ci): add a release-body CI. Signed-off-by: Federico Di Pierro --- .github/workflows/kernel_tests.yaml | 6 ++-- .github/workflows/release-body.yml | 52 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release-body.yml diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index e3bb257937..6fbfc4a583 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -11,9 +11,11 @@ on: push: branches: - master + tags: + - '[0-9]+.[0-9]+.[0-9]+\+driver' concurrency: - group: kernel-tests-master + group: kernel-tests cancel-in-progress: true jobs: @@ -34,7 +36,7 @@ jobs: working-directory: ./ansible-playbooks run: | LIBS_V=${{ github.event.inputs.libsversion }} - LIBS_VERSION=${LIBS_V:-"master"} + LIBS_VERSION=${LIBS_V:-${{ github.ref_name }}} cat > vars.yml < release-body.md + + - name: Release + uses: softprops/action-gh-release@v1 + with: + body_path: ./release-body.md + generate_release_notes: true From 4d91fa1b8954bc3b8f95daa5496e998c15269f98 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Thu, 27 Jul 2023 14:33:46 +0200 Subject: [PATCH 58/66] fix(ci): fixed release-body workflow to avoid using ed. Signed-off-by: Federico Di Pierro --- .github/workflows/release-body.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release-body.yml b/.github/workflows/release-body.yml index 0714beb1b1..7a765f1102 100644 --- a/.github/workflows/release-body.yml +++ b/.github/workflows/release-body.yml @@ -23,11 +23,6 @@ jobs: name: matrix_* name_is_regexp: true run_id: ${{ github.event.workflow_run.id }} - - - name: Install deps - run: | - sudo apt update - sudo apt install -y --no-install-recommends ed # Steps: # Remove everything after the table (ie: since the first line that starts with "# ", @@ -37,8 +32,8 @@ jobs: # Finally, merge them together - name: Append matrixes to create release body run: | - printf '%s\n' '/# /,$d' 'q' '.a' '' '.' 'wq' | ed -s matrix_X64.md - printf '%s\n' '/# /,$d' 'q' '.a' '' '.' 'wq' | ed -s matrix_ARM64.md + sed -i -n '/# /q;p' matrix_X64.md + sed -i -n '/# /q;p' matrix_ARM64.md sed -i 's/\[\(.\)\]([^)]*)/\1/g' matrix_X64.md sed -i 's/\[\(.\)\]([^)]*)/\1/g' matrix_ARM64.md sed -i '1s/^/# Driver Testing Matrix amd64\n\n/' matrix_X64.md From 1a5a031ea4686ea6dc954bdada62ec39bd2c9794 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Thu, 27 Jul 2023 15:31:54 +0200 Subject: [PATCH 59/66] fix(ci): fixed release-body matrixes path. Signed-off-by: Federico Di Pierro --- .github/workflows/release-body.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-body.yml b/.github/workflows/release-body.yml index 7a765f1102..90cb40a164 100644 --- a/.github/workflows/release-body.yml +++ b/.github/workflows/release-body.yml @@ -32,6 +32,7 @@ jobs: # Finally, merge them together - name: Append matrixes to create release body run: | + mv matrix_*/*.md . sed -i -n '/# /q;p' matrix_X64.md sed -i -n '/# /q;p' matrix_ARM64.md sed -i 's/\[\(.\)\]([^)]*)/\1/g' matrix_X64.md From 5cb1ad2409f422fdb58d06f2b4a63881b7d4f8af Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Thu, 27 Jul 2023 16:43:49 +0200 Subject: [PATCH 60/66] fix(ci): force tag_name in release-body workflow. Signed-off-by: Federico Di Pierro --- .github/workflows/release-body.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-body.yml b/.github/workflows/release-body.yml index 90cb40a164..8d2fed160d 100644 --- a/.github/workflows/release-body.yml +++ b/.github/workflows/release-body.yml @@ -46,3 +46,4 @@ jobs: with: body_path: ./release-body.md generate_release_notes: true + tag_name: ${{ github.event.workflow_run.head_branch }} From a846e57bf2ffa960b025e6e337ee36f873400de9 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Fri, 28 Jul 2023 09:26:10 +0200 Subject: [PATCH 61/66] fix(ci): release-body to only trigger when kernel_test was triggered on release branches. Signed-off-by: Federico Di Pierro --- .github/workflows/release-body.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-body.yml b/.github/workflows/release-body.yml index 8d2fed160d..4377987d8f 100644 --- a/.github/workflows/release-body.yml +++ b/.github/workflows/release-body.yml @@ -3,7 +3,7 @@ on: workflow_run: workflows: ["Test drivers against a matrix of kernels/distros"] types: [completed] - branches-ignore: [master] # only when it runs on tags + branches: ['release/[0-9]+.[0-9]+.x'] # only match real release branches permissions: contents: write From 5cbdaa1965ef61620fbbe4adb843f930ab594e55 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Fri, 28 Jul 2023 09:34:14 +0200 Subject: [PATCH 62/66] chore(ci): always append matrixes to existing body. This way, even if user tagging the release autogenerated the release body, we attach the matrixes after the text. Signed-off-by: Federico Di Pierro --- .github/workflows/release-body.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-body.yml b/.github/workflows/release-body.yml index 4377987d8f..798f43e69f 100644 --- a/.github/workflows/release-body.yml +++ b/.github/workflows/release-body.yml @@ -45,5 +45,5 @@ jobs: uses: softprops/action-gh-release@v1 with: body_path: ./release-body.md - generate_release_notes: true + append_body: true tag_name: ${{ github.event.workflow_run.head_branch }} From 135e4a1a7817c454eda07317425bb0cc3db3203a Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 28 Jul 2023 10:25:51 +0000 Subject: [PATCH 63/66] fix(userspace/libsinsp): make sinsp struct size independent from compilation flags Signed-off-by: Jason Dellaluce --- userspace/libsinsp/fdinfo.cpp | 8 +++--- userspace/libsinsp/fdinfo.h | 6 ++--- userspace/libsinsp/parsers.cpp | 16 +++++------ userspace/libsinsp/sinsp.cpp | 45 ++++++++++++++++++++----------- userspace/libsinsp/sinsp.h | 23 ++++------------ userspace/libsinsp/threadinfo.cpp | 16 +++++------ 6 files changed, 57 insertions(+), 57 deletions(-) diff --git a/userspace/libsinsp/fdinfo.cpp b/userspace/libsinsp/fdinfo.cpp index ab5b59b6d3..28503049d0 100644 --- a/userspace/libsinsp/fdinfo.cpp +++ b/userspace/libsinsp/fdinfo.cpp @@ -359,7 +359,7 @@ sinsp_fdinfo_t* sinsp_fdtable::add(int64_t fd, sinsp_fdinfo_t* fdinfo) // m_last_accessed_fd = -1; #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_added_fds++; + m_inspector->m_stats->m_n_added_fds++; #endif std::pair::iterator, bool> insert_res = m_table.emplace(fd, *fdinfo); return &(insert_res.first->second); @@ -429,15 +429,15 @@ void sinsp_fdtable::erase(int64_t fd) // ASSERT(false); #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_failed_fd_lookups++; + m_inspector->m_stats->m_n_failed_fd_lookups++; #endif } else { m_table.erase(fdit); #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_noncached_fd_lookups++; - m_inspector->m_stats.m_n_removed_fds++; + m_inspector->m_stats->m_n_noncached_fd_lookups++; + m_inspector->m_stats->m_n_removed_fds++; #endif } } diff --git a/userspace/libsinsp/fdinfo.h b/userspace/libsinsp/fdinfo.h index a5f11fb87f..4770cf531a 100644 --- a/userspace/libsinsp/fdinfo.h +++ b/userspace/libsinsp/fdinfo.h @@ -525,7 +525,7 @@ class sinsp_fdtable if(m_last_accessed_fd != -1 && fd == m_last_accessed_fd) { #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_cached_fd_lookups++; + m_inspector->m_stats->m_n_cached_fd_lookups++; #endif return m_last_accessed_fdinfo; } @@ -538,14 +538,14 @@ class sinsp_fdtable if(fdit == m_table.end()) { #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_failed_fd_lookups++; + m_inspector->m_stats->m_n_failed_fd_lookups++; #endif return NULL; } else { #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_noncached_fd_lookups++; + m_inspector->m_stats->m_n_noncached_fd_lookups++; #endif m_last_accessed_fd = fd; m_last_accessed_fdinfo = &(fdit->second); diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index cf883c6078..a44dd12604 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -934,7 +934,7 @@ void sinsp_parser::store_event(sinsp_evt *evt) // to drop the information it carries. // #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_store_drops++; + m_inspector->m_stats->m_n_store_drops++; #endif return; } @@ -969,7 +969,7 @@ void sinsp_parser::store_event(sinsp_evt *evt) tinfo->m_lastevent_cpuid = evt->get_cpuid(); #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_stored_evts++; + m_inspector->m_stats->m_n_stored_evts++; #endif } @@ -993,7 +993,7 @@ bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_ev // can be truncated // #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_retrieve_drops++; + m_inspector->m_stats->m_n_retrieve_drops++; #endif return false; } @@ -1015,7 +1015,7 @@ bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_ev enter_evt->get_type() == PPME_SYSCALL_EXECVEAT_E) { #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_retrieved_evts++; + m_inspector->m_stats->m_n_retrieved_evts++; #endif return true; } @@ -1029,13 +1029,13 @@ bool sinsp_parser::retrieve_enter_event(sinsp_evt *enter_evt, sinsp_evt *exit_ev //ASSERT(false); exit_evt->m_tinfo->set_lastevent_data_validity(false); #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_retrieve_drops++; + m_inspector->m_stats->m_n_retrieve_drops++; #endif return false; } #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_retrieved_evts++; + m_inspector->m_stats->m_n_retrieved_evts++; #endif return true; } @@ -3681,12 +3681,12 @@ void sinsp_parser::parse_close_exit(sinsp_evt *evt) // increment of m_n_failed_fd_lookups (for the enter event too if there's one). // #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_failed_fd_lookups--; + m_inspector->m_stats->m_n_failed_fd_lookups--; #endif if(evt->m_tinfo && evt->m_tinfo->is_lastevent_data_valid()) { #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_failed_fd_lookups--; + m_inspector->m_stats->m_n_failed_fd_lookups--; #endif } } diff --git a/userspace/libsinsp/sinsp.cpp b/userspace/libsinsp/sinsp.cpp index a1790e145e..fe1c774a31 100644 --- a/userspace/libsinsp/sinsp.cpp +++ b/userspace/libsinsp/sinsp.cpp @@ -40,16 +40,22 @@ limitations under the License. #include "plugin_filtercheck.h" #include "strl.h" -#ifndef CYGWING_AGENT -#ifndef MINIMAL_BUILD +#if !defined(CYGWING_AGENT) +#if !defined(MINIMAL_BUILD) #include "k8s_api_handler.h" -#endif // MINIMAL_BUILD -#ifdef HAS_CAPTURE -#ifndef MINIMAL_BUILD +#if defined(HAS_CAPTURE) #include -#endif // MINIMAL_BUILD -#endif -#endif +#endif // defined(HAS_CAPTURE) +#else // !defined(MINIMAL_BUILD) +struct k8s_api_handler{}; // note: makes the unique_ptr static asserts happy +#endif // !defined(MINIMAL_BUILD) +#endif // !defined(CYGWING_AGENT) + +#ifdef GATHER_INTERNAL_STATS +#include "stats.h" +#else // GATHER_INTERNAL_STATS +struct sinsp_stats{}; // note: makes the unique_ptr static asserts happy +#endif // GATHER_INTERNAL_STATS #ifdef HAS_ANALYZER #include "analyzer_int.h" @@ -72,6 +78,9 @@ sinsp::sinsp(bool static_container, const std::string &static_id, const std::str m_host_root(scap_get_host_root()), m_container_manager(this, static_container, static_id, static_name, static_image), m_usergroup_manager(this), + m_k8s_api_handler(nullptr), + m_k8s_ext_handler(nullptr), + m_stats(nullptr), m_suppressed_comms(), m_inited(false) { @@ -128,6 +137,10 @@ sinsp::sinsp(bool static_container, const std::string &static_id, const std::str m_self_pid = getpid(); #endif +#ifdef GATHER_INTERNAL_STATS + m_stats = std::make_unique; +#endif + m_proc_scan_timeout_ms = SCAP_PROC_SCAN_TIMEOUT_NONE; m_proc_scan_log_interval_ms = SCAP_PROC_SCAN_LOG_NONE; @@ -348,7 +361,7 @@ void sinsp::init() // Basic inits // #ifdef GATHER_INTERNAL_STATS - m_stats.clear(); + m_stats->clear(); #endif m_nevts = 0; @@ -1978,15 +1991,15 @@ sinsp_stats sinsp::get_stats() { scap_get_stats(m_h, &stats); - m_stats.m_n_seen_evts = stats.n_evts; - m_stats.m_n_drops = stats.n_drops; - m_stats.m_n_preemptions = stats.n_preemptions; + m_stats->m_n_seen_evts = stats.n_evts; + m_stats->m_n_drops = stats.n_drops; + m_stats->m_n_preemptions = stats.n_preemptions; } else { - m_stats.m_n_seen_evts = 0; - m_stats.m_n_drops = 0; - m_stats.m_n_preemptions = 0; + m_stats->m_n_seen_evts = 0; + m_stats->m_n_drops = 0; + m_stats->m_n_preemptions = 0; } // @@ -2002,7 +2015,7 @@ sinsp_stats sinsp::get_stats() // Return the result // - return m_stats; + return *m_stats.get(); } #endif // GATHER_INTERNAL_STATS diff --git a/userspace/libsinsp/sinsp.h b/userspace/libsinsp/sinsp.h index 4181ae06e1..520f3941ee 100644 --- a/userspace/libsinsp/sinsp.h +++ b/userspace/libsinsp/sinsp.h @@ -70,7 +70,6 @@ limitations under the License. #include "event.h" #include "filter.h" #include "dumper.h" -#include "stats.h" #include "ifinfo.h" #include "container.h" #include "user.h" @@ -107,23 +106,20 @@ class sinsp_analyzer; class sinsp_filter; class cycle_writer; class sinsp_protodecoder; -#if !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD) class k8s; -#endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD) class sinsp_partial_tracer; class mesos; class sinsp_plugin; class sinsp_plugin_manager; class sinsp_observer; +class sinsp_stats; -#if defined(HAS_CAPTURE) && !defined(_WIN32) class sinsp_ssl; class sinsp_bearer_token; template class socket_data_handler; template class socket_collector; class k8s_handler; class k8s_api_handler; -#endif // HAS_CAPTURE std::vector sinsp_split(const std::string &s, char delim); @@ -1138,12 +1134,10 @@ VISIBILITY_PRIVATE // // Kubernetes // -#if !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD) std::string* m_k8s_api_server; std::string* m_k8s_api_cert; std::string* m_k8s_node_name; bool m_k8s_node_name_validated = false; -#ifdef HAS_CAPTURE std::shared_ptr m_k8s_ssl; std::shared_ptr m_k8s_bt; std::unique_ptr m_k8s_api_handler; @@ -1157,10 +1151,8 @@ VISIBILITY_PRIVATE "replicasets" }; bool m_k8s_ext_detect_done = false; -#endif // HAS_CAPTURE k8s* m_k8s_client; uint64_t m_k8s_last_watch_time_ns; -#endif // !defined(CYGWING_AGENT) && !defined(MINIMAL_BUILD) // // Mesos/Marathon @@ -1190,12 +1182,9 @@ VISIBILITY_PRIVATE // // Internal stats // -#ifdef GATHER_INTERNAL_STATS - sinsp_stats m_stats; -#endif -#ifdef HAS_ANALYZER + std::unique_ptr m_stats; + std::vector m_tid_collisions; -#endif // // Saved snaplen @@ -1242,12 +1231,10 @@ VISIBILITY_PRIVATE cycle_writer* m_cycle_writer; bool m_write_cycling; -#ifdef SIMULATE_DROP_MODE // // Some dropping infrastructure // bool m_isdropping; -#endif // // App events @@ -1290,9 +1277,9 @@ VISIBILITY_PRIVATE uint64_t m_next_stats_print_time_ns; static unsigned int m_num_possible_cpus; -#if defined(HAS_CAPTURE) + int64_t m_self_pid; -#endif + // // /proc scan parameters diff --git a/userspace/libsinsp/threadinfo.cpp b/userspace/libsinsp/threadinfo.cpp index 56dfda2177..ad965f98f7 100644 --- a/userspace/libsinsp/threadinfo.cpp +++ b/userspace/libsinsp/threadinfo.cpp @@ -1414,11 +1414,11 @@ void sinsp_thread_manager::clear() m_n_drops = 0; #ifdef GATHER_INTERNAL_STATS - m_failed_lookups = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_failed_lookups","Failed thread lookups")); - m_cached_lookups = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_cached_lookups","Cached thread lookups")); - m_non_cached_lookups = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_non_cached_lookups","Non cached thread lookups")); - m_added_threads = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_added","Number of added threads")); - m_removed_threads = &m_inspector->m_stats.get_metrics_registry().register_counter(internal_metrics::metric_name("thread_removed","Removed threads")); + m_failed_lookups = &m_inspector->m_stats->get_metrics_registry().register_counter(internal_metrics::metric_name("thread_failed_lookups","Failed thread lookups")); + m_cached_lookups = &m_inspector->m_stats->get_metrics_registry().register_counter(internal_metrics::metric_name("thread_cached_lookups","Cached thread lookups")); + m_non_cached_lookups = &m_inspector->m_stats->get_metrics_registry().register_counter(internal_metrics::metric_name("thread_non_cached_lookups","Non cached thread lookups")); + m_added_threads = &m_inspector->m_stats->get_metrics_registry().register_counter(internal_metrics::metric_name("thread_added","Number of added threads")); + m_removed_threads = &m_inspector->m_stats->get_metrics_registry().register_counter(internal_metrics::metric_name("thread_removed","Removed threads")); #endif } @@ -1666,9 +1666,9 @@ void sinsp_thread_manager::recreate_child_dependencies() void sinsp_thread_manager::update_statistics() { #ifdef GATHER_INTERNAL_STATS - m_inspector->m_stats.m_n_threads = get_thread_count(); + m_inspector->m_stats->m_n_threads = get_thread_count(); - m_inspector->m_stats.m_n_fds = 0; + m_inspector->m_stats->m_n_fds = 0; for(threadinfo_map_iterator_t it = m_threadtable.begin(); it != m_threadtable.end(); it++) { sinsp_fdtable* fd_table_ptr = it->second.get_fd_table(); @@ -1677,7 +1677,7 @@ void sinsp_thread_manager::update_statistics() ASSERT(false); return; } - m_inspector->m_stats.m_n_fds += fd_table_ptr->size(); + m_inspector->m_stats->m_n_fds += fd_table_ptr->size(); } #endif } From 25533bd420470545b6b2e13fb96a3b28909c4b53 Mon Sep 17 00:00:00 2001 From: Jason Dellaluce Date: Fri, 28 Jul 2023 11:25:51 +0000 Subject: [PATCH 64/66] fix(userspace/libsinsp): solve ambiguous move casting Signed-off-by: Jason Dellaluce --- userspace/libsinsp/plugin_table_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/userspace/libsinsp/plugin_table_api.cpp b/userspace/libsinsp/plugin_table_api.cpp index 1651fb6abb..a9841bf0b2 100755 --- a/userspace/libsinsp/plugin_table_api.cpp +++ b/userspace/libsinsp/plugin_table_api.cpp @@ -599,7 +599,7 @@ struct plugin_table_wrapper: public libsinsp::state::table } entry->m_entry = res; entry->m_detached = false; - return std::move(e); + return std::shared_ptr(std::move(e)); } bool erase_entry(const KeyType& key) override From d07890e4b9af5bc145c56eb7ec25db60e92c7900 Mon Sep 17 00:00:00 2001 From: Federico Di Pierro Date: Fri, 28 Jul 2023 14:55:25 +0200 Subject: [PATCH 65/66] update(ci): bumped kernel_tests to v0.2.3 Signed-off-by: Federico Di Pierro --- .github/workflows/kernel_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/kernel_tests.yaml b/.github/workflows/kernel_tests.yaml index 6fbfc4a583..952bdc2e3b 100644 --- a/.github/workflows/kernel_tests.yaml +++ b/.github/workflows/kernel_tests.yaml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@v3 with: repository: falcosecurity/kernel-testing - ref: v0.2.2 + ref: v0.2.3 - name: Generate vars yaml working-directory: ./ansible-playbooks From abcede0ef5db3fd68afa451e29f2ce2fd0aa714d Mon Sep 17 00:00:00 2001 From: Matthew Knight Date: Thu, 27 Jul 2023 17:13:07 -0700 Subject: [PATCH 66/66] fix off-by-one bug in cgroup v1 parser Signed-off-by: Matthew Knight --- userspace/libscap/linux/scap_cgroup.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/userspace/libscap/linux/scap_cgroup.c b/userspace/libscap/linux/scap_cgroup.c index 82ccc91a95..b0fc6f0e7c 100644 --- a/userspace/libscap/linux/scap_cgroup.c +++ b/userspace/libscap/linux/scap_cgroup.c @@ -338,8 +338,10 @@ static int32_t get_cgroup_subsystems_v1(struct scap_cgroup_set* subsystems) fclose(cgroups); return SCAP_FAILURE; } + // 3:cpu,cpuacct:/user.slice/user-0.slice/session-13542.scope - // ^p ^q + // ^p ^q + p += 1; char* q = strchr(p, ':'); if(!q) { @@ -348,7 +350,7 @@ static int32_t get_cgroup_subsystems_v1(struct scap_cgroup_set* subsystems) } // 3:cpu,cpuacct - // ^p ^q + // ^p ^q *q = 0; if(strlen(p) == 0) { @@ -358,7 +360,7 @@ static int32_t get_cgroup_subsystems_v1(struct scap_cgroup_set* subsystems) while(1) { // 3:cpu\0cpuacct - // ^p ^q + // ^p ^q char* comma = strchr(p, ','); if(comma) {