@cryptotaxi247 / netdata-1 / commits / 132e31ce9

eBPF unittest + bug fix (#15146)

thiagoftsm committed Jun 12, 2023 at 14:35 UTC 132e31ce9e9a56c40c481071ee8993a0607508cc
8 files changed +180 -21
CMakeLists.txt
+2
@@ -620,6 +620,8 @@ set(EBPF_PROCESS_PLUGIN_FILES
620 collectors/ebpf.plugin/ebpf_apps.h
621 collectors/ebpf.plugin/ebpf_cgroup.c
622 collectors/ebpf.plugin/ebpf_cgroup.h
623 + collectors/ebpf.plugin/ebpf_unittest.c
624 + collectors/ebpf.plugin/ebpf_unittest.h
625 )
626
627 set(PROC_PLUGIN_FILES
Makefile.am
+2
@@ -360,6 +360,8 @@ EBPF_PLUGIN_FILES = \
360 collectors/ebpf.plugin/ebpf_apps.h \
361 collectors/ebpf.plugin/ebpf_cgroup.c \
362 collectors/ebpf.plugin/ebpf_cgroup.h \
363 + collectors/ebpf.plugin/ebpf_unittest.c \
364 + collectors/ebpf.plugin/ebpf_unittest.h \
365 $(LIBNETDATA_FILES) \
366 $(NULL)
367
collectors/ebpf.plugin/ebpf.c
+75 -16
@@ -6,6 +6,7 @@
6
7 #include "ebpf.h"
8 #include "ebpf_socket.h"
9 +#include "ebpf_unittest.h"
10 #include "libnetdata/required_dummies.h"
11
12 /*****************************************************************
@@ -578,7 +579,7 @@ static void ebpf_exit()
579 * @param objects objects loaded from eBPF programs
580 * @param probe_links links from loader
581 */
581 -static void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links)
582 +void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links)
583 {
584 if (!probe_links || !objects)
585 return;
@@ -2059,6 +2060,48 @@ static inline void ebpf_load_thread_config()
2060 }
2061 }
2062
2063 +/**
2064 + * Check Conditions
2065 + *
2066 + * This function checks kernel that plugin is running and permissions.
2067 + *
2068 + * @return It returns 0 on success and -1 otherwise
2069 + */
2070 +int ebpf_check_conditions()
2071 +{
2072 + if (!has_condition_to_run(running_on_kernel)) {
2073 + error("The current collector cannot run on this kernel.");
2074 + return -1;
2075 + }
2076 +
2077 + if (!am_i_running_as_root()) {
2078 + error(
2079 + "ebpf.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities..",
2080 + (unsigned int)getuid(), (unsigned int)geteuid());
2081 + return -1;
2082 + }
2083 +
2084 + return 0;
2085 +}
2086 +
2087 +/**
2088 + * Adjust memory
2089 + *
2090 + * Adjust memory values to load eBPF programs.
2091 + *
2092 + * @return It returns 0 on success and -1 otherwise
2093 + */
2094 +int ebpf_adjust_memory_limit()
2095 +{
2096 + struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY };
2097 + if (setrlimit(RLIMIT_MEMLOCK, &r)) {
2098 + error("Setrlimit(RLIMIT_MEMLOCK)");
2099 + return -1;
2100 + }
2101 +
2102 + return 0;
2103 +}
2104 +
2105 /**
2106 * Parse arguments given from user.
2107 *
@@ -2097,6 +2140,7 @@ static void ebpf_parse_args(int argc, char **argv)
2140 {"return", no_argument, 0, 0 },
2141 {"legacy", no_argument, 0, 0 },
2142 {"core", no_argument, 0, 0 },
2143 + {"unittest", no_argument, 0, 0 },
2144 {0, 0, 0, 0}
2145 };
2146
@@ -2287,6 +2331,33 @@ static void ebpf_parse_args(int argc, char **argv)
2331 #endif
2332 break;
2333 }
2334 + case EBPF_OPTION_UNITTEST: {
2335 + // if we cannot run until the end, we will cancel the unittest
2336 + int exit_code = ECANCELED;
2337 + if (ebpf_check_conditions())
2338 + goto unittest;
2339 +
2340 + if (ebpf_adjust_memory_limit())
2341 + goto unittest;
2342 +
2343 + // Load binary in entry mode
2344 + ebpf_ut_initialize_structure(MODE_ENTRY);
2345 + if (ebpf_ut_load_real_binary())
2346 + goto unittest;
2347 +
2348 + ebpf_ut_cleanup_memory();
2349 +
2350 + // Do not load a binary in entry mode
2351 + ebpf_ut_initialize_structure(MODE_ENTRY);
2352 + if (ebpf_ut_load_fake_binary())
2353 + goto unittest;
2354 +
2355 + ebpf_ut_cleanup_memory();
2356 +
2357 + exit_code = 0;
2358 +unittest:
2359 + exit(exit_code);
2360 + }
2361 default: {
2362 break;
2363 }
@@ -2505,17 +2576,8 @@ int main(int argc, char **argv)
2576 ebpf_parse_args(argc, argv);
2577 ebpf_manage_pid(getpid());
2578
2508 - if (!has_condition_to_run(running_on_kernel)) {
2509 - error("The current collector cannot run on this kernel.");
2579 + if (ebpf_check_conditions())
2580 return 2;
2511 - }
2512 -
2513 - if (!am_i_running_as_root()) {
2514 - error(
2515 - "ebpf.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities..",
2516 - (unsigned int)getuid(), (unsigned int)geteuid());
2517 - return 3;
2518 - }
2581
2582 // set name
2583 program_name = "ebpf.plugin";
@@ -2527,11 +2589,8 @@ int main(int argc, char **argv)
2589 error_log_errors_per_period = 100;
2590 error_log_throttle_period = 3600;
2591
2530 - struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY };
2531 - if (setrlimit(RLIMIT_MEMLOCK, &r)) {
2532 - error("Setrlimit(RLIMIT_MEMLOCK)");
2533 - return 4;
2534 - }
2592 + if (ebpf_adjust_memory_limit())
2593 + return 3;
2594
2595 signal(SIGINT, ebpf_stop_threads);
2596 signal(SIGQUIT, ebpf_stop_threads);
collectors/ebpf.plugin/ebpf.h
+4 -1
@@ -119,7 +119,8 @@ enum ebpf_main_index {
119 EBPF_OPTION_GLOBAL_CHART,
120 EBPF_OPTION_RETURN_MODE,
121 EBPF_OPTION_LEGACY,
122 - EBPF_OPTION_CORE
122 + EBPF_OPTION_CORE,
123 + EBPF_OPTION_UNITTEST
124 };
125
126 typedef struct ebpf_tracepoint {
@@ -308,6 +309,8 @@ void ebpf_write_chart_obsolete(char *type, char *id, char *title, char *units, c
309 void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, char **dimensions, uint32_t end);
310 void ebpf_update_disabled_plugin_stats(ebpf_module_t *em);
311 ARAL *ebpf_allocate_pid_aral(char *name, size_t size);
312 +void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links);
313 +
314 extern ebpf_filesystem_partitions_t localfs[];
315 extern ebpf_sync_syscalls_t local_syscalls[];
316 extern int ebpf_exit_plugin;
collectors/ebpf.plugin/ebpf_process.c
+1 -3
@@ -1265,8 +1265,7 @@ void *ebpf_process_thread(void *ptr)
1265 set_local_pointers();
1266 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
1267 if (!em->probe_links) {
1268 - pthread_mutex_unlock(&lock);
1269 - goto endprocess;
1268 + em->enabled = em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1269 }
1270
1271 int algorithms[NETDATA_KEY_PUBLISH_PROCESS_END] = {
@@ -1295,7 +1294,6 @@ void *ebpf_process_thread(void *ptr)
1294
1295 process_collector(em);
1296
1298 -endprocess:
1297 pthread_mutex_lock(&ebpf_exit_cleanup);
1298 if (em->enabled == NETDATA_THREAD_EBPF_RUNNING)
1299 ebpf_update_disabled_plugin_stats(em);
collectors/ebpf.plugin/ebpf_unittest.c new
+83
@@ -0,0 +1,83 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "ebpf_unittest.h"
4 +
5 +ebpf_module_t test_em;
6 +
7 +/**
8 + * Initialize structure
9 + *
10 + * Initialize structure used to run unittests
11 + */
12 +void ebpf_ut_initialize_structure(netdata_run_mode_t mode)
13 +{
14 + memset(&test_em, 0, sizeof(ebpf_module_t));
15 + test_em.thread_name = strdupz("process");
16 + test_em.config_name = test_em.thread_name;
17 + test_em.kernels = NETDATA_V3_10 | NETDATA_V4_14 | NETDATA_V4_16 | NETDATA_V4_18 | NETDATA_V5_4 | NETDATA_V5_10 |
18 + NETDATA_V5_14;
19 + test_em.pid_map_size = ND_EBPF_DEFAULT_PID_SIZE;
20 + test_em.apps_level = NETDATA_APPS_LEVEL_REAL_PARENT;
21 + test_em.mode = mode;
22 +}
23 +
24 +/**
25 + * Clean UP Memory
26 + *
27 + * Clean up allocated data during unit test;
28 + */
29 +void ebpf_ut_cleanup_memory()
30 +{
31 + freez((void *)test_em.thread_name);
32 +}
33 +
34 +/**
35 + * Load Binary
36 + *
37 + * Test load of legacy eBPF programs.
38 + *
39 + * @return It returns 0 on success and -1 otherwise.
40 + */
41 +static int ebpf_ut_load_binary()
42 +{
43 + test_em.probe_links = ebpf_load_program(ebpf_plugin_dir, &test_em, running_on_kernel, isrh, &test_em.objects);
44 + if (!test_em.probe_links)
45 + return -1;
46 +
47 + ebpf_unload_legacy_code(test_em.objects, test_em.probe_links);
48 +
49 + return 0;
50 +}
51 +
52 +/**
53 + * Load Real Binary
54 + *
55 + * Load an existent binary inside plugin directory.
56 + *
57 + * @return It returns 0 on success and -1 otherwise.
58 + */
59 +int ebpf_ut_load_real_binary()
60 +{
61 + return ebpf_ut_load_binary();
62 +}
63 +/**
64 + * Load fake Binary
65 + *
66 + * Try to load a binary not generated by netdata.
67 + *
68 + * @return It returns 0 on success and -1 otherwise. The success for this function means we could work properly with
69 + * expected fails.
70 + */
71 +int ebpf_ut_load_fake_binary()
72 +{
73 + const char *original = test_em.thread_name;
74 +
75 + test_em.thread_name = strdupz("I_am_not_here");
76 + int ret = ebpf_ut_load_binary();
77 +
78 + ebpf_ut_cleanup_memory();
79 +
80 + test_em.thread_name = original;
81 +
82 + return !ret;
83 +}
collectors/ebpf.plugin/ebpf_unittest.h new
+10
@@ -0,0 +1,10 @@
1 +#ifndef NETDATA_EBPF_PLUGIN_UNITTEST_H_
2 +# define NETDATA_EBPF_PLUGIN_UNITTEST_H_ 1
3 +
4 +#include "ebpf.h"
5 +
6 +void ebpf_ut_initialize_structure(netdata_run_mode_t mode);
7 +int ebpf_ut_load_real_binary();
8 +int ebpf_ut_load_fake_binary();
9 +void ebpf_ut_cleanup_memory();
10 +#endif
libnetdata/ebpf/ebpf.c
+3 -1
@@ -856,8 +856,10 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv
856 em->load |= EBPF_LOAD_LEGACY;
857
858 *obj = bpf_object__open_file(lpath, NULL);
859 + if (!*obj)
860 + return NULL;
861 +
862 if (libbpf_get_error(obj)) {
860 - error("Cannot open BPF object %s", lpath);
863 bpf_object__close(*obj);
864 return NULL;
865 }