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
}