mirror of
https://github.com/netdata/netdata.git
synced 2025-04-28 06:32:30 +00:00
eBPF unittest + bug fix (#15146)
This commit is contained in:
parent
3e30218863
commit
132e31ce9e
8 changed files with 180 additions and 21 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -2059,6 +2060,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.
|
||||
*
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
83
collectors/ebpf.plugin/ebpf_unittest.c
Normal file
83
collectors/ebpf.plugin/ebpf_unittest.c
Normal file
|
@ -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;
|
||||
}
|
10
collectors/ebpf.plugin/ebpf_unittest.h
Normal file
10
collectors/ebpf.plugin/ebpf_unittest.h
Normal file
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue