diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | collectors/ebpf.plugin/ebpf.c | 91 | ||||
-rw-r--r-- | collectors/ebpf.plugin/ebpf.h | 5 | ||||
-rw-r--r-- | collectors/ebpf.plugin/ebpf_process.c | 4 | ||||
-rw-r--r-- | collectors/ebpf.plugin/ebpf_unittest.c | 83 | ||||
-rw-r--r-- | collectors/ebpf.plugin/ebpf_unittest.h | 10 | ||||
-rw-r--r-- | libnetdata/ebpf/ebpf.c | 4 |
8 files changed, 180 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 566053faa1..34c381619a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -620,6 +620,8 @@ set(EBPF_PROCESS_PLUGIN_FILES collectors/ebpf.plugin/ebpf_apps.h collectors/ebpf.plugin/ebpf_cgroup.c collectors/ebpf.plugin/ebpf_cgroup.h + collectors/ebpf.plugin/ebpf_unittest.c + collectors/ebpf.plugin/ebpf_unittest.h ) set(PROC_PLUGIN_FILES diff --git a/Makefile.am b/Makefile.am index 434038a442..666847dc32 100644 --- a/Makefile.am +++ b/Makefile.am @@ -360,6 +360,8 @@ EBPF_PLUGIN_FILES = \ collectors/ebpf.plugin/ebpf_apps.h \ collectors/ebpf.plugin/ebpf_cgroup.c \ collectors/ebpf.plugin/ebpf_cgroup.h \ + collectors/ebpf.plugin/ebpf_unittest.c \ + collectors/ebpf.plugin/ebpf_unittest.h \ $(LIBNETDATA_FILES) \ $(NULL) diff --git a/collectors/ebpf.plugin/ebpf.c b/collectors/ebpf.plugin/ebpf.c index ea53a3ba59..45303574fa 100644 --- a/collectors/ebpf.plugin/ebpf.c +++ b/collectors/ebpf.plugin/ebpf.c @@ -6,6 +6,7 @@ #include "ebpf.h" #include "ebpf_socket.h" +#include "ebpf_unittest.h" #include "libnetdata/required_dummies.h" /***************************************************************** @@ -578,7 +579,7 @@ static void ebpf_exit() * @param objects objects loaded from eBPF programs * @param probe_links links from loader */ -static void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links) +void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links) { if (!probe_links || !objects) return; @@ -2060,6 +2061,48 @@ static inline void ebpf_load_thread_config() } /** + * Check Conditions + * + * This function checks kernel that plugin is running and permissions. + * + * @return It returns 0 on success and -1 otherwise + */ +int ebpf_check_conditions() +{ + if (!has_condition_to_run(running_on_kernel)) { + error("The current collector cannot run on this kernel."); + return -1; + } + + if (!am_i_running_as_root()) { + error( + "ebpf.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities..", + (unsigned int)getuid(), (unsigned int)geteuid()); + return -1; + } + + return 0; +} + +/** + * Adjust memory + * + * Adjust memory values to load eBPF programs. + * + * @return It returns 0 on success and -1 otherwise + */ +int ebpf_adjust_memory_limit() +{ + struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY }; + if (setrlimit(RLIMIT_MEMLOCK, &r)) { + error("Setrlimit(RLIMIT_MEMLOCK)"); + return -1; + } + + return 0; +} + +/** * Parse arguments given from user. * * @param argc the number of arguments @@ -2097,6 +2140,7 @@ static void ebpf_parse_args(int argc, char **argv) {"return", no_argument, 0, 0 }, {"legacy", no_argument, 0, 0 }, {"core", no_argument, 0, 0 }, + {"unittest", no_argument, 0, 0 }, {0, 0, 0, 0} }; @@ -2287,6 +2331,33 @@ static void ebpf_parse_args(int argc, char **argv) #endif break; } + case EBPF_OPTION_UNITTEST: { + // if we cannot run until the end, we will cancel the unittest + int exit_code = ECANCELED; + if (ebpf_check_conditions()) + goto unittest; + + if (ebpf_adjust_memory_limit()) + goto unittest; + + // Load binary in entry mode + ebpf_ut_initialize_structure(MODE_ENTRY); + if (ebpf_ut_load_real_binary()) + goto unittest; + + ebpf_ut_cleanup_memory(); + + // Do not load a binary in entry mode + ebpf_ut_initialize_structure(MODE_ENTRY); + if (ebpf_ut_load_fake_binary()) + goto unittest; + + ebpf_ut_cleanup_memory(); + + exit_code = 0; +unittest: + exit(exit_code); + } default: { break; } @@ -2505,17 +2576,8 @@ int main(int argc, char **argv) ebpf_parse_args(argc, argv); ebpf_manage_pid(getpid()); - if (!has_condition_to_run(running_on_kernel)) { - error("The current collector cannot run on this kernel."); + if (ebpf_check_conditions()) return 2; - } - - if (!am_i_running_as_root()) { - error( - "ebpf.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities..", - (unsigned int)getuid(), (unsigned int)geteuid()); - return 3; - } // set name program_name = "ebpf.plugin"; @@ -2527,11 +2589,8 @@ int main(int argc, char **argv) error_log_errors_per_period = 100; error_log_throttle_period = 3600; - struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY }; - if (setrlimit(RLIMIT_MEMLOCK, &r)) { - error("Setrlimit(RLIMIT_MEMLOCK)"); - return 4; - } + if (ebpf_adjust_memory_limit()) + return 3; signal(SIGINT, ebpf_stop_threads); signal(SIGQUIT, ebpf_stop_threads); diff --git a/collectors/ebpf.plugin/ebpf.h b/collectors/ebpf.plugin/ebpf.h index f9a19233c3..ae24c302c5 100644 --- a/collectors/ebpf.plugin/ebpf.h +++ b/collectors/ebpf.plugin/ebpf.h @@ -119,7 +119,8 @@ enum ebpf_main_index { EBPF_OPTION_GLOBAL_CHART, EBPF_OPTION_RETURN_MODE, EBPF_OPTION_LEGACY, - EBPF_OPTION_CORE + EBPF_OPTION_CORE, + EBPF_OPTION_UNITTEST }; typedef struct ebpf_tracepoint { @@ -308,6 +309,8 @@ void ebpf_write_chart_obsolete(char *type, char *id, char *title, char *units, c void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, char **dimensions, uint32_t end); void ebpf_update_disabled_plugin_stats(ebpf_module_t *em); ARAL *ebpf_allocate_pid_aral(char *name, size_t size); +void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links); + extern ebpf_filesystem_partitions_t localfs[]; extern ebpf_sync_syscalls_t local_syscalls[]; extern int ebpf_exit_plugin; diff --git a/collectors/ebpf.plugin/ebpf_process.c b/collectors/ebpf.plugin/ebpf_process.c index 2878dbe2db..17a9809d3c 100644 --- a/collectors/ebpf.plugin/ebpf_process.c +++ b/collectors/ebpf.plugin/ebpf_process.c @@ -1265,8 +1265,7 @@ void *ebpf_process_thread(void *ptr) set_local_pointers(); em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects); if (!em->probe_links) { - pthread_mutex_unlock(&lock); - goto endprocess; + em->enabled = em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING; } int algorithms[NETDATA_KEY_PUBLISH_PROCESS_END] = { @@ -1295,7 +1294,6 @@ void *ebpf_process_thread(void *ptr) process_collector(em); -endprocess: pthread_mutex_lock(&ebpf_exit_cleanup); if (em->enabled == NETDATA_THREAD_EBPF_RUNNING) ebpf_update_disabled_plugin_stats(em); diff --git a/collectors/ebpf.plugin/ebpf_unittest.c b/collectors/ebpf.plugin/ebpf_unittest.c new file mode 100644 index 0000000000..3e1443ad37 --- /dev/null +++ b/collectors/ebpf.plugin/ebpf_unittest.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "ebpf_unittest.h" + +ebpf_module_t test_em; + +/** + * Initialize structure + * + * Initialize structure used to run unittests + */ +void ebpf_ut_initialize_structure(netdata_run_mode_t mode) +{ + memset(&test_em, 0, sizeof(ebpf_module_t)); + test_em.thread_name = strdupz("process"); + test_em.config_name = test_em.thread_name; + test_em.kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_10 | + NETDATA_V5_14; + test_em.pid_map_size = ND_EBPF_DEFAULT_PID_SIZE; + test_em.apps_level = NETDATA_APPS_LEVEL_REAL_PARENT; + test_em.mode = mode; +} + +/** + * Clean UP Memory + * + * Clean up allocated data during unit test; + */ +void ebpf_ut_cleanup_memory() +{ + freez((void *)test_em.thread_name); +} + +/** + * Load Binary + * + * Test load of legacy eBPF programs. + * + * @return It returns 0 on success and -1 otherwise. + */ +static int ebpf_ut_load_binary() +{ + test_em.probe_links = ebpf_load_program(ebpf_plugin_dir, &test_em, running_on_kernel, isrh, &test_em.objects); + if (!test_em.probe_links) + return -1; + + ebpf_unload_legacy_code(test_em.objects, test_em.probe_links); + + return 0; +} + +/** + * Load Real Binary + * + * Load an existent binary inside plugin directory. + * + * @return It returns 0 on success and -1 otherwise. + */ +int ebpf_ut_load_real_binary() +{ + return ebpf_ut_load_binary(); +} +/** + * Load fake Binary + * + * Try to load a binary not generated by netdata. + * + * @return It returns 0 on success and -1 otherwise. The success for this function means we could work properly with + * expected fails. + */ +int ebpf_ut_load_fake_binary() +{ + const char *original = test_em.thread_name; + + test_em.thread_name = strdupz("I_am_not_here"); + int ret = ebpf_ut_load_binary(); + + ebpf_ut_cleanup_memory(); + + test_em.thread_name = original; + + return !ret; +} diff --git a/collectors/ebpf.plugin/ebpf_unittest.h b/collectors/ebpf.plugin/ebpf_unittest.h new file mode 100644 index 0000000000..429cbe6288 --- /dev/null +++ b/collectors/ebpf.plugin/ebpf_unittest.h @@ -0,0 +1,10 @@ +#ifndef NETDATA_EBPF_PLUGIN_UNITTEST_H_ +# define NETDATA_EBPF_PLUGIN_UNITTEST_H_ 1 + +#include "ebpf.h" + +void ebpf_ut_initialize_structure(netdata_run_mode_t mode); +int ebpf_ut_load_real_binary(); +int ebpf_ut_load_fake_binary(); +void ebpf_ut_cleanup_memory(); +#endif diff --git a/libnetdata/ebpf/ebpf.c b/libnetdata/ebpf/ebpf.c index caaefc3207..b980d09eda 100644 --- a/libnetdata/ebpf/ebpf.c +++ b/libnetdata/ebpf/ebpf.c @@ -856,8 +856,10 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv em->load |= EBPF_LOAD_LEGACY; *obj = bpf_object__open_file(lpath, NULL); + if (!*obj) + return NULL; + if (libbpf_get_error(obj)) { - error("Cannot open BPF object %s", lpath); bpf_object__close(*obj); return NULL; } |