@cryptotaxi247 / netdata-1 / commits / 1cd58c154

Adjust eBPF (user ring) (#21676)

thiagoftsm committed Mar 18, 2026 at 21:53 UTC 1cd58c15445aab4727063d3b6f034f687a5db171
47 files changed +6202 -4611
CMakeLists.txt
+5 -3
@@ -2912,9 +2912,11 @@ if(ENABLE_PLUGIN_EBPF)
2912 src/collectors/ebpf.plugin/ebpf_unittest.h
2913 src/collectors/ebpf.plugin/ebpf_functions.c
2914 src/collectors/ebpf.plugin/ebpf_functions.h
2915 - src/collectors/ebpf.plugin/libbpf_api/ebpf.c
2916 - src/collectors/ebpf.plugin/libbpf_api/ebpf.h
2917 - )
2915 + src/collectors/ebpf.plugin/libbpf_api/ebpf.c
2916 + src/collectors/ebpf.plugin/libbpf_api/ebpf.h
2917 + src/collectors/ebpf.plugin/libbpf_api/ebpf_library.c
2918 + src/collectors/ebpf.plugin/libbpf_api/ebpf_library.h
2919 + )
2920
2921 add_executable(ebpf.plugin ${EBPF_PLUGIN_FILES} ${INTERCOMMUNICATION_COLLECTORS_FILES})
2922 target_link_libraries(ebpf.plugin libnetdata)
src/collectors/collectors-ipc/ebpf-ipc.c
+68 -64
@@ -9,40 +9,44 @@ sem_t *shm_mutex_ebpf_integration = SEM_FAILED;
9 static Pvoid_t ebpf_ipc_JudyL = NULL;
10 ebpf_user_mem_stat_t ebpf_stat_values;
11
12 -bool using_vector = false;
13 -
12 static uint32_t *ebpf_shm_find_index_unsafe(uint32_t pid)
13 {
16 - uint32_t *ret = NULL;
14 Pvoid_t *Pvalue = JudyLGet(ebpf_ipc_JudyL, (Word_t)pid, PJE0);
15 if (Pvalue)
19 - ret = *Pvalue;
20 - return ret;
16 + return *Pvalue;
17 + return NULL;
18 }
19
23 -static bool ebpf_find_pid_shm_del_unsafe(uint32_t pid, enum ebpf_pids_index idx)
20 +static bool ebpf_find_pid_shm_del_unsafe(uint32_t pid, enum ebpf_pids_index shm_idx)
21 {
22 uint32_t *lpid = ebpf_shm_find_index_unsafe(pid);
26 - if (!lpid || !ebpf_stat_values.current)
23 + if (!lpid)
24 + return false;
25 +
26 + uint32_t idx = *lpid;
27 + if (idx >= ebpf_stat_values.current)
28 + return false;
29 +
30 + netdata_ebpf_pid_stats_t *ptr = &integration_shm[idx];
31 + if (!ptr->threads)
32 return false;
33
29 - netdata_ebpf_pid_stats_t *ptr = &integration_shm[*lpid];
30 - ptr->threads &= ~(idx << 1);
31 - if (ptr->threads) {
34 + ptr->threads &= ~(1UL << (shm_idx << 1));
35 + if (ptr->threads)
36 return true;
33 - }
37
38 + freez(lpid);
39 (void)JudyLDel(&ebpf_ipc_JudyL, (Word_t)pid, PJE0);
36 -
40 ebpf_stat_values.current--;
38 - if (!ebpf_stat_values.current)
41 +
42 + if (idx == ebpf_stat_values.current)
43 return false;
44
41 - netdata_ebpf_pid_stats_t *newValue = &integration_shm[ebpf_stat_values.current];
42 - uint32_t *move = ebpf_shm_find_index_unsafe(newValue->pid);
43 - if (move) {
44 - *move = *lpid;
45 - memcpy(ptr, newValue, sizeof(*ptr));
45 + uint32_t last_pid = integration_shm[ebpf_stat_values.current].pid;
46 + uint32_t *last_lpid = ebpf_shm_find_index_unsafe(last_pid);
47 + if (last_lpid) {
48 + *last_lpid = idx;
49 + memcpy(ptr, &integration_shm[ebpf_stat_values.current], sizeof(*ptr));
50 }
51
52 return false;
@@ -51,16 +55,21 @@ static bool ebpf_find_pid_shm_del_unsafe(uint32_t pid, enum ebpf_pids_index idx)
55 static uint32_t ebpf_find_or_create_index_pid(uint32_t pid)
56 {
57 uint32_t *idx = ebpf_shm_find_index_unsafe(pid);
54 - if (!idx) {
55 - Pvoid_t *Pvalue = JudyLIns(&ebpf_ipc_JudyL, (Word_t)pid, PJE0);
56 - internal_fatal(!Pvalue || Pvalue == PJERR, "EBPF: pid judy index");
57 - if (likely(!*Pvalue)) {
58 - *Pvalue = idx = callocz(1, sizeof(*idx));
59 - *idx = ebpf_stat_values.current++;
60 - } else
61 - idx = *Pvalue;
62 - }
63 - return *idx;
58 + if (idx)
59 + return *idx;
60 +
61 + if (ebpf_stat_values.current >= ebpf_stat_values.total)
62 + return UINT32_MAX;
63 +
64 + Pvoid_t *Pvalue = JudyLIns(&ebpf_ipc_JudyL, (Word_t)pid, PJE0);
65 + internal_fatal(!Pvalue || Pvalue == PJERR, "EBPF: pid judy index");
66 +
67 + uint32_t new_idx = ebpf_stat_values.current++;
68 + uint32_t *stored_idx = callocz(1, sizeof(uint32_t));
69 + *stored_idx = new_idx;
70 + *Pvalue = stored_idx;
71 +
72 + return new_idx;
73 }
74
75 bool netdata_ebpf_reset_shm_pointer_unsafe(int fd, uint32_t pid, enum ebpf_pids_index idx)
@@ -68,40 +77,21 @@ bool netdata_ebpf_reset_shm_pointer_unsafe(int fd, uint32_t pid, enum ebpf_pids_
77 if (idx != NETDATA_EBPF_PIDS_SOCKET_IDX)
78 bpf_map_delete_elem(fd, &pid);
79
71 - if (using_vector && integration_shm) {
72 - netdata_ebpf_pid_stats_t *ptr = &integration_shm[pid];
73 - ptr->threads &= ~(idx << 1);
74 - if (!ptr->threads) {
75 - ebpf_stat_values.current--;
76 - memset(ptr, 0, sizeof(*ptr));
77 - return false;
78 - }
79 - } else {
80 - return ebpf_find_pid_shm_del_unsafe(pid, idx);
81 - }
82 -
83 - return true;
80 + return ebpf_find_pid_shm_del_unsafe(pid, idx);
81 }
82
83 netdata_ebpf_pid_stats_t *netdata_ebpf_get_shm_pointer_unsafe(uint32_t pid, enum ebpf_pids_index idx)
84 {
88 - if (!integration_shm || (ebpf_stat_values.current + 1) == ebpf_stat_values.total)
85 + if (!integration_shm || ebpf_stat_values.current >= ebpf_stat_values.total)
86 return NULL;
87
91 - if (!using_vector) {
92 - pid = ebpf_find_or_create_index_pid(pid);
93 - }
94 -
95 - if (pid >= ebpf_stat_values.total)
88 + uint32_t shm_idx = ebpf_find_or_create_index_pid(pid);
89 + if (shm_idx == UINT32_MAX || shm_idx >= ebpf_stat_values.total)
90 return NULL;
91
98 - netdata_ebpf_pid_stats_t *ptr = &integration_shm[pid];
99 - if (using_vector && !ptr->threads) {
100 - ebpf_stat_values.current++;
101 - }
102 -
92 + netdata_ebpf_pid_stats_t *ptr = &integration_shm[shm_idx];
93 ptr->pid = pid;
104 - ptr->threads |= idx << 1;
94 + ptr->threads |= (1UL << (idx << 1));
95
96 return ptr;
97 }
@@ -115,26 +105,32 @@ void netdata_integration_cleanup_shm()
105 if (integration_shm) {
106 size_t length = ebpf_stat_values.total * sizeof(netdata_ebpf_pid_stats_t);
107 nd_munmap(integration_shm, length);
108 + integration_shm = NULL;
109 }
110
111 + Word_t index = 0;
112 + Word_t next_index;
113 + PPvoid_t pid_ptr;
114 + while ((pid_ptr = JudyLFirst(ebpf_ipc_JudyL, &index, PJE0)) != NULL) {
115 + uint32_t *pid = *(uint32_t **)pid_ptr;
116 + next_index = index;
117 + freez(pid);
118 + JudyLDel(&ebpf_ipc_JudyL, next_index, PJE0);
119 + index = next_index;
120 + }
121 + ebpf_ipc_JudyL = NULL;
122 +
123 if (shm_fd_ebpf_integration > 0) {
124 close(shm_fd_ebpf_integration);
125 + shm_fd_ebpf_integration = -1;
126 }
127 }
128
125 -static void netdata_ebpf_select_access_mode(size_t pids)
126 -{
127 - size_t local_max = os_get_system_pid_max();
128 - using_vector = (pids == local_max);
129 -}
130 -
129 int netdata_integration_initialize_shm(size_t pids)
130 {
131 if (!pids)
132 return -1;
133
136 - netdata_ebpf_select_access_mode(pids);
137 -
134 shm_fd_ebpf_integration = shm_open(NETDATA_EBPF_INTEGRATION_NAME, O_CREAT | O_RDWR, 0660);
135 if (shm_fd_ebpf_integration < 0) {
136 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot initialize shared memory. Integration won't happen.");
@@ -150,11 +146,12 @@ int netdata_integration_initialize_shm(size_t pids)
146 }
147
148 integration_shm = nd_mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd_ebpf_integration, 0);
153 - if (!integration_shm) {
149 + if (integration_shm == MAP_FAILED) {
150 nd_log(
151 NDLS_COLLECTORS,
152 NDLP_ERR,
153 "Cannot map shared memory used between cgroup and eBPF, integration won't happen");
154 + integration_shm = NULL;
155 goto end_shm;
156 }
157
@@ -165,10 +162,17 @@ int netdata_integration_initialize_shm(size_t pids)
162 }
163
164 nd_log(NDLS_COLLECTORS, NDLP_ERR, "Cannot create semaphore, integration between won't happen");
168 - nd_munmap(integration_shm, length);
169 - integration_shm = NULL;
165
166 end_shm:
167 + if (integration_shm) {
168 + size_t unmap_len = ebpf_stat_values.total * sizeof(netdata_ebpf_pid_stats_t);
169 + nd_munmap(integration_shm, unmap_len);
170 + integration_shm = NULL;
171 + }
172 + if (shm_fd_ebpf_integration > 0) {
173 + close(shm_fd_ebpf_integration);
174 + shm_fd_ebpf_integration = -1;
175 + }
176 return -1;
177 }
178
src/collectors/collectors-ipc/ebpf-ipc.h
+5 -5
@@ -160,8 +160,8 @@ typedef struct netdata_publish_cachestat {
160
161 typedef struct netdata_publish_dcstat_pid {
162 uint64_t cache_access;
163 - uint32_t file_system;
164 - uint32_t not_found;
163 + uint64_t file_system;
164 + uint64_t not_found;
165 } netdata_publish_dcstat_pid_t;
166
167 typedef struct netdata_publish_dcstat {
@@ -181,9 +181,9 @@ typedef struct netdata_dcstat_pid {
181 uint32_t gid;
182 char name[TASK_COMM_LEN];
183
184 - uint32_t cache_access;
185 - uint32_t file_system;
186 - uint32_t not_found;
184 + uint64_t cache_access;
185 + uint64_t file_system;
186 + uint64_t not_found;
187 } netdata_dcstat_pid_t;
188
189 typedef struct __attribute__((packed)) netdata_publish_swap {
src/collectors/ebpf.plugin/ebpf.c
+348 -2381
@@ -3,11 +3,15 @@
3 #include <sys/time.h>
4 #include <sys/resource.h>
5 #include <ifaddrs.h>
6 +#include <errno.h>
7 +#include <stdint.h>
8
9 #include "ebpf.h"
10 #include "ebpf_socket.h"
11 #include "ebpf_unittest.h"
12 +#include "libbpf_api/ebpf_library.h"
13 #include "libnetdata/required_dummies.h"
14 +#include "libnetdata/libjudy/judy-malloc.h"
15
16 /*****************************************************************
17 *
@@ -27,7 +31,7 @@ int isrh = 0;
31 int main_thread_id = 0;
32 int process_pid_fd = -1;
33 uint64_t collect_pids = 0;
30 -static uint32_t integration_with_collectors = NETDATA_EBPF_INTEGRATION_DISABLED;
34 +uint32_t integration_with_collectors = NETDATA_EBPF_INTEGRATION_DISABLED;
35 ND_THREAD *socket_ipc = NULL;
36 static size_t global_iterations_counter = 1;
37 bool publish_internal_metrics = true;
@@ -46,11 +50,16 @@ struct netdata_static_thread cgroup_integration_thread = {
50 .init_routine = NULL,
51 .start_routine = NULL};
52
53 +static void ebpf_socket_unload_bpf(ebpf_module_t *em);
54 +
55 ebpf_module_t ebpf_modules[] = {
56 {.info =
57 {.thread_name = "process", .config_name = "process", .thread_description = NETDATA_EBPF_MODULE_PROCESS_DESC},
58 .functions =
53 - {.start_routine = ebpf_process_thread, .apps_routine = ebpf_process_create_apps_charts, .fnct_routine = NULL},
59 + {.start_routine = ebpf_process_thread,
60 + .apps_routine = ebpf_process_create_apps_charts,
61 + .fnct_routine = NULL,
62 + .bpf_unload = ebpf_unload_legacy_bpf},
63 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
64 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
65 .global_charts = 1,
@@ -79,6 +88,7 @@ ebpf_module_t ebpf_modules[] = {
88 {.start_routine = ebpf_socket_thread,
89 .apps_routine = ebpf_socket_create_apps_charts,
90 .fnct_routine = ebpf_socket_read_open_connections,
91 + .bpf_unload = ebpf_socket_unload_bpf,
92 .fcnt_name = EBPF_FUNCTION_SOCKET,
93 .fcnt_desc = EBPF_PLUGIN_SOCKET_FUNCTION_DESCRIPTION,
94 .fcnt_thread_chart_name = NULL,
@@ -112,7 +122,8 @@ ebpf_module_t ebpf_modules[] = {
122 .functions =
123 {.start_routine = ebpf_cachestat_thread,
124 .apps_routine = ebpf_cachestat_create_apps_charts,
115 - .fnct_routine = NULL},
125 + .fnct_routine = NULL,
126 + .bpf_unload = ebpf_cachestat_unload_bpf},
127 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
128 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
129 .global_charts = 1,
@@ -137,7 +148,11 @@ ebpf_module_t ebpf_modules[] = {
148 .lifetime = EBPF_DEFAULT_LIFETIME,
149 .running_time = 0},
150 {.info = {.thread_name = "sync", .config_name = "sync", .thread_description = NETDATA_EBPF_SYNC_MODULE_DESC},
140 - .functions = {.start_routine = ebpf_sync_thread, .apps_routine = NULL, .fnct_routine = NULL},
151 + .functions =
152 + {.start_routine = ebpf_sync_thread,
153 + .apps_routine = NULL,
154 + .fnct_routine = NULL,
155 + .bpf_unload = ebpf_sync_unload_bpf},
156 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
157 .maps = NULL,
158 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
@@ -163,7 +178,10 @@ ebpf_module_t ebpf_modules[] = {
178 .running_time = 0},
179 {.info = {.thread_name = "dc", .config_name = "dc", .thread_description = NETDATA_EBPF_DC_MODULE_DESC},
180 .functions =
166 - {.start_routine = ebpf_dcstat_thread, .apps_routine = ebpf_dcstat_create_apps_charts, .fnct_routine = NULL},
181 + {.start_routine = ebpf_dcstat_thread,
182 + .apps_routine = ebpf_dcstat_create_apps_charts,
183 + .fnct_routine = NULL,
184 + .bpf_unload = ebpf_dcstat_unload_bpf},
185 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
186 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
187 .global_charts = 1,
@@ -188,7 +206,10 @@ ebpf_module_t ebpf_modules[] = {
206 .running_time = 0},
207 {.info = {.thread_name = "swap", .config_name = "swap", .thread_description = NETDATA_EBPF_SWAP_MODULE_DESC},
208 .functions =
191 - {.start_routine = ebpf_swap_thread, .apps_routine = ebpf_swap_create_apps_charts, .fnct_routine = NULL},
209 + {.start_routine = ebpf_swap_thread,
210 + .apps_routine = ebpf_swap_create_apps_charts,
211 + .fnct_routine = NULL,
212 + .bpf_unload = ebpf_swap_unload_bpf},
213 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
214 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
215 .global_charts = 1,
@@ -213,7 +234,11 @@ ebpf_module_t ebpf_modules[] = {
234 .lifetime = EBPF_DEFAULT_LIFETIME,
235 .running_time = 0},
236 {.info = {.thread_name = "vfs", .config_name = "vfs", .thread_description = NETDATA_EBPF_VFS_MODULE_DESC},
216 - .functions = {.start_routine = ebpf_vfs_thread, .apps_routine = ebpf_vfs_create_apps_charts, .fnct_routine = NULL},
237 + .functions =
238 + {.start_routine = ebpf_vfs_thread,
239 + .apps_routine = ebpf_vfs_create_apps_charts,
240 + .fnct_routine = NULL,
241 + .bpf_unload = ebpf_vfs_unload_bpf},
242 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
243 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
244 .global_charts = 1,
@@ -238,7 +263,11 @@ ebpf_module_t ebpf_modules[] = {
263 .running_time = 0},
264 {.info =
265 {.thread_name = "filesystem", .config_name = "filesystem", .thread_description = NETDATA_EBPF_FS_MODULE_DESC},
241 - .functions = {.start_routine = ebpf_filesystem_thread, .apps_routine = NULL, .fnct_routine = NULL},
266 + .functions =
267 + {.start_routine = ebpf_filesystem_thread,
268 + .apps_routine = NULL,
269 + .fnct_routine = NULL,
270 + .bpf_unload = ebpf_filesystem_unload_bpf},
271 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
272 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
273 .global_charts = 1,
@@ -263,7 +292,11 @@ ebpf_module_t ebpf_modules[] = {
292 .lifetime = EBPF_DEFAULT_LIFETIME,
293 .running_time = 0},
294 {.info = {.thread_name = "disk", .config_name = "disk", .thread_description = NETDATA_EBPF_DISK_MODULE_DESC},
266 - .functions = {.start_routine = ebpf_disk_thread, .apps_routine = NULL, .fnct_routine = NULL},
295 + .functions =
296 + {.start_routine = ebpf_disk_thread,
297 + .apps_routine = NULL,
298 + .fnct_routine = NULL,
299 + .bpf_unload = ebpf_unload_legacy_bpf},
300 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
301 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
302 .global_charts = 1,
@@ -287,7 +320,11 @@ ebpf_module_t ebpf_modules[] = {
320 .lifetime = EBPF_DEFAULT_LIFETIME,
321 .running_time = 0},
322 {.info = {.thread_name = "mount", .config_name = "mount", .thread_description = NETDATA_EBPF_MOUNT_MODULE_DESC},
290 - .functions = {.start_routine = ebpf_mount_thread, .apps_routine = NULL, .fnct_routine = NULL},
323 + .functions =
324 + {.start_routine = ebpf_mount_thread,
325 + .apps_routine = NULL,
326 + .fnct_routine = NULL,
327 + .bpf_unload = ebpf_mount_unload_bpf},
328 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
329 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
330 .global_charts = 1,
@@ -311,7 +348,11 @@ ebpf_module_t ebpf_modules[] = {
348 .lifetime = EBPF_DEFAULT_LIFETIME,
349 .running_time = 0},
350 {.info = {.thread_name = "fd", .config_name = "fd", .thread_description = NETDATA_EBPF_FD_MODULE_DESC},
314 - .functions = {.start_routine = ebpf_fd_thread, .apps_routine = ebpf_fd_create_apps_charts, .fnct_routine = NULL},
351 + .functions =
352 + {.start_routine = ebpf_fd_thread,
353 + .apps_routine = ebpf_fd_create_apps_charts,
354 + .fnct_routine = NULL,
355 + .bpf_unload = ebpf_fd_unload_bpf},
356 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
357 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
358 .global_charts = 1,
@@ -362,7 +403,11 @@ ebpf_module_t ebpf_modules[] = {
403 .running_time = 0},
404 {.info =
405 {.thread_name = "softirq", .config_name = "softirq", .thread_description = NETDATA_EBPF_SOFTIRQ_MODULE_DESC},
365 - .functions = {.start_routine = ebpf_softirq_thread, .apps_routine = NULL, .fnct_routine = NULL},
406 + .functions =
407 + {.start_routine = ebpf_softirq_thread,
408 + .apps_routine = NULL,
409 + .fnct_routine = NULL,
410 + .bpf_unload = ebpf_unload_legacy_bpf},
411 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
412 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
413 .global_charts = 1,
@@ -388,7 +433,10 @@ ebpf_module_t ebpf_modules[] = {
433 {.info =
434 {.thread_name = "oomkill", .config_name = "oomkill", .thread_description = NETDATA_EBPF_OOMKILL_MODULE_DESC},
435 .functions =
391 - {.start_routine = ebpf_oomkill_thread, .apps_routine = ebpf_oomkill_create_apps_charts, .fnct_routine = NULL},
436 + {.start_routine = ebpf_oomkill_thread,
437 + .apps_routine = ebpf_oomkill_create_apps_charts,
438 + .fnct_routine = NULL,
439 + .bpf_unload = ebpf_unload_legacy_bpf},
440 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
441 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
442 .global_charts = 1,
@@ -412,7 +460,11 @@ ebpf_module_t ebpf_modules[] = {
460 .lifetime = EBPF_DEFAULT_LIFETIME,
461 .running_time = 0},
462 {.info = {.thread_name = "shm", .config_name = "shm", .thread_description = NETDATA_EBPF_SHM_MODULE_DESC},
415 - .functions = {.start_routine = ebpf_shm_thread, .apps_routine = ebpf_shm_create_apps_charts, .fnct_routine = NULL},
463 + .functions =
464 + {.start_routine = ebpf_shm_thread,
465 + .apps_routine = ebpf_shm_create_apps_charts,
466 + .fnct_routine = NULL,
467 + .bpf_unload = ebpf_shm_unload_bpf},
468 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
469 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
470 .global_charts = 1,
@@ -436,7 +488,11 @@ ebpf_module_t ebpf_modules[] = {
488 .lifetime = EBPF_DEFAULT_LIFETIME,
489 .running_time = 0},
490 {.info = {.thread_name = "mdflush", .config_name = "mdflush", .thread_description = NETDATA_EBPF_MD_MODULE_DESC},
439 - .functions = {.start_routine = ebpf_mdflush_thread, .apps_routine = NULL, .fnct_routine = NULL},
491 + .functions =
492 + {.start_routine = ebpf_mdflush_thread,
493 + .apps_routine = NULL,
494 + .fnct_routine = NULL,
495 + .bpf_unload = ebpf_unload_legacy_bpf},
496 .enabled = NETDATA_THREAD_EBPF_NOT_RUNNING,
497 .update_every = EBPF_DEFAULT_UPDATE_EVERY,
498 .global_charts = 1,
@@ -701,7 +757,7 @@ ebpf_filesystem_partitions_t localfs[] = {
757 "nfs_file_write",
758 "nfs_open",
759 "nfs_getattr",
704 - NULL}}, // // "nfs4_file_open" - not present on all kernels
760 + NULL}}, // "nfs4_file_open" - not present on all kernels
761 {.filesystem = "zfs",
762 .optional_filesystem = NULL,
763 .family = "zfs",
@@ -819,6 +875,8 @@ ebpf_plugin_stats_t plugin_statistics = {
875 .hash_tables = 0};
876 netdata_ebpf_judy_pid_t ebpf_judy_pid = {.pid_table = NULL, .index = {.JudyLArray = NULL}};
877 bool ebpf_plugin_exit = false;
878 +volatile sig_atomic_t ebpf_stop_signal = 0;
879 +static bool ebpf_pre_exit_check_done = false;
880
881 #ifdef LIBBPF_MAJOR_VERSION
882 struct btf *default_btf = NULL;
@@ -831,2417 +889,324 @@ struct mdflush_bpf *mdflush_bpf_obj = NULL;
889 struct mount_bpf *mount_bpf_obj = NULL;
890 struct shm_bpf *shm_bpf_obj = NULL;
891 struct socket_bpf *socket_bpf_obj = NULL;
834 -struct swap_bpf *bpf_obj = NULL;
892 +struct swap_bpf *swap_bpf_obj = NULL;
893 struct vfs_bpf *vfs_bpf_obj = NULL;
894 struct process_bpf *process_bpf_obj = NULL;
895 #else
896 void *default_btf = NULL;
839 -#endif
840 -const char *btf_path = NULL;
841 -
842 -/*****************************************************************
843 - *
844 - * FUNCTIONS USED TO MANIPULATE JUDY ARRAY
845 - *
846 - *****************************************************************/
847 -
848 -/**
849 - * Hashtable insert unsafe
850 - *
851 - * Find or create a value associated to the index
852 - *
853 - * @return The lsocket = 0 when new item added to the array otherwise the existing item value is returned in *lsocket
854 - * we return a pointer to a pointer, so that the caller can put anything needed at the value of the index.
855 - * The pointer to pointer we return has to be used before any other operation that may change the index (insert/delete).
856 - *
857 - */
858 -void **ebpf_judy_insert_unsafe(PPvoid_t arr, Word_t key)
859 -{
860 - JError_t J_Error;
861 - Pvoid_t *idx = JudyLIns(arr, key, &J_Error);
862 - if (unlikely(idx == PJERR)) {
863 - netdata_log_error(
864 - "Cannot add PID to JudyL, JU_ERRNO_* == %u, ID == %d", JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
865 - }
866 -
867 - return idx;
868 -}
869 -
870 -/**
871 - * Get PID from judy
872 - *
873 - * Get a pointer for the `pid` from judy_array;
874 - *
875 - * @param judy_array a judy array where PID is the primary key
876 - * @param pid pid stored.
877 - */
878 -netdata_ebpf_judy_pid_stats_t *ebpf_get_pid_from_judy_unsafe(PPvoid_t judy_array, uint32_t pid)
879 -{
880 - netdata_ebpf_judy_pid_stats_t **pid_pptr =
881 - (netdata_ebpf_judy_pid_stats_t **)ebpf_judy_insert_unsafe(judy_array, pid);
882 - netdata_ebpf_judy_pid_stats_t *pid_ptr = *pid_pptr;
883 - if (likely(*pid_pptr == NULL)) {
884 - // a new PID added to the index
885 - *pid_pptr = aral_mallocz(ebpf_judy_pid.pid_table);
886 -
887 - pid_ptr = *pid_pptr;
888 -
889 - pid_ptr->cmdline = NULL;
890 - pid_ptr->socket_stats.JudyLArray = NULL;
891 - rw_spinlock_init(&pid_ptr->socket_stats.rw_spinlock);
892 - }
893 -
894 - return pid_ptr;
895 -}
896 -
897 -/*****************************************************************
898 - *
899 - * FUNCTIONS USED TO ALLOCATE APPS/CGROUP MEMORIES (ARAL)
900 - *
901 - *****************************************************************/
902 -
903 -/**
904 - * Allocate PID ARAL
905 - *
906 - * Allocate memory using ARAL functions to speed up processing.
907 - *
908 - * @param name the internal name used for allocated region.
909 - * @param size size of each element inside allocated space
910 - *
911 - * @return It returns the address on success and NULL otherwise.
912 - */
913 -ARAL *ebpf_allocate_pid_aral(char *name, size_t size)
914 -{
915 - static size_t max_elements = NETDATA_EBPF_ALLOC_MAX_PID;
916 - if (max_elements < NETDATA_EBPF_ALLOC_MIN_ELEMENTS) {
917 - netdata_log_error(
918 - "Number of elements given is too small, adjusting it for %d", NETDATA_EBPF_ALLOC_MIN_ELEMENTS);
919 - max_elements = NETDATA_EBPF_ALLOC_MIN_ELEMENTS;
920 - }
921 -
922 - return aral_create(name, size, 0, 0, NULL, NULL, NULL, false, false, false);
923 -}
924 -
925 -/*****************************************************************
926 - *
927 - * FUNCTIONS USED TO CLEAN MEMORY AND OPERATE SYSTEM FILES
928 - *
929 - *****************************************************************/
930 -
931 -/**
932 - * Wait to avoid possible coredumps while process is closing.
933 - */
934 -static inline void ebpf_check_before2go()
935 -{
936 - int i = EBPF_OPTION_ALL_CHARTS;
937 - usec_t max = USEC_PER_SEC, step = 200000;
938 - while (i && max) {
939 - max -= step;
940 - sleep_usec(step);
941 - i = 0;
942 - int j;
943 - netdata_mutex_lock(&ebpf_exit_cleanup);
944 - for (j = 0; ebpf_modules[j].info.thread_name != NULL; j++) {
945 - if (ebpf_modules[j].enabled < NETDATA_THREAD_EBPF_STOPPING)
946 - i++;
947 - }
948 - netdata_mutex_unlock(&ebpf_exit_cleanup);
949 - }
950 -
951 - if (i) {
952 - netdata_log_error("eBPF cannot unload all threads on time, but it will go away");
953 - }
954 -}
955 -
956 -/**
957 - * Close the collector gracefully
958 - */
959 -static void ebpf_exit()
960 -{
961 -#ifdef LIBBPF_MAJOR_VERSION
962 - netdata_mutex_lock(&ebpf_exit_cleanup);
963 - if (default_btf) {
964 - btf__free(default_btf);
965 - default_btf = NULL;
966 - }
967 - netdata_mutex_unlock(&ebpf_exit_cleanup);
968 -#endif
969 -
970 - char filename[FILENAME_MAX + 1];
971 - ebpf_pid_file(filename, FILENAME_MAX);
972 - if (unlink(filename))
973 - netdata_log_error("Cannot remove PID file %s", filename);
974 -
975 -#ifdef NETDATA_INTERNAL_CHECKS
976 - netdata_log_error("Good bye world! I was PID %d", main_thread_id);
977 -#endif
978 - fprintf(stdout, "EXIT\n");
979 - fflush(stdout);
980 -
981 - ebpf_check_before2go();
982 - netdata_mutex_lock(&mutex_cgroup_shm);
983 - if (shm_ebpf_cgroup.header) {
984 - ebpf_unmap_cgroup_shared_memory();
985 - shm_unlink(NETDATA_SHARED_MEMORY_EBPF_CGROUP_NAME);
986 - }
987 - netdata_mutex_unlock(&mutex_cgroup_shm);
988 - netdata_integration_cleanup_shm();
989 -
990 - exit(0);
991 -}
992 -
993 -/**
994 - * Unload loegacy code
995 - *
996 - * @param objects objects loaded from eBPF programs
997 - * @param probe_links links from loader
998 - */
999 -void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links)
1000 -{
1001 - if (!probe_links || !objects)
1002 - return;
1003 -
1004 - struct bpf_program *prog;
1005 - size_t j = 0;
1006 - bpf_object__for_each_program(prog, objects)
1007 - {
1008 - bpf_link__destroy(probe_links[j]);
1009 - j++;
1010 - }
1011 - freez(probe_links);
1012 - if (objects)
1013 - bpf_object__close(objects);
1014 -}
1015 -
1016 -/**
1017 - * Unload Unique maps
1018 - *
1019 - * This function unload all BPF maps from threads using one unique BPF object.
1020 - */
1021 -static void ebpf_unload_unique_maps()
1022 -{
1023 - int i;
1024 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
1025 - // These threads are cleaned with other functions
1026 - if (i != EBPF_MODULE_SOCKET_IDX)
1027 - continue;
1028 -
1029 - if (ebpf_modules[i].enabled != NETDATA_THREAD_EBPF_STOPPED) {
1030 - if (ebpf_modules[i].enabled != NETDATA_THREAD_EBPF_NOT_RUNNING)
1031 - netdata_log_error(
1032 - "Cannot unload maps for thread %s, because it is not stopped.", ebpf_modules[i].info.thread_name);
1033 -
1034 - continue;
1035 - }
1036 -
1037 - if (ebpf_modules[i].load == EBPF_LOAD_LEGACY) {
1038 - ebpf_unload_legacy_code(ebpf_modules[i].objects, ebpf_modules[i].probe_links);
1039 - continue;
1040 - }
1041 -
1042 -#ifdef LIBBPF_MAJOR_VERSION
1043 - if (socket_bpf_obj)
1044 - socket_bpf__destroy(socket_bpf_obj);
1045 -#endif
1046 - }
1047 -}
1048 -
1049 -/**
1050 - * Unload filesystem maps
1051 - *
1052 - * This function unload all BPF maps from filesystem thread.
1053 - */
1054 -static void ebpf_unload_filesystems()
1055 -{
1056 - if (ebpf_modules[EBPF_MODULE_FILESYSTEM_IDX].enabled == NETDATA_THREAD_EBPF_NOT_RUNNING ||
1057 - ebpf_modules[EBPF_MODULE_FILESYSTEM_IDX].enabled < NETDATA_THREAD_EBPF_STOPPING ||
1058 - ebpf_modules[EBPF_MODULE_FILESYSTEM_IDX].load != EBPF_LOAD_LEGACY)
1059 - return;
1060 -
1061 - int i;
1062 - for (i = 0; localfs[i].filesystem != NULL; i++) {
1063 - if (!localfs[i].objects)
1064 - continue;
1065 -
1066 - ebpf_unload_legacy_code(localfs[i].objects, localfs[i].probe_links);
1067 - }
1068 -}
1069 -
1070 -/**
1071 - * Unload sync maps
1072 - *
1073 - * This function unload all BPF maps from sync thread.
1074 - */
1075 -static void ebpf_unload_sync()
1076 -{
1077 - if (ebpf_modules[EBPF_MODULE_SYNC_IDX].enabled == NETDATA_THREAD_EBPF_NOT_RUNNING ||
1078 - ebpf_modules[EBPF_MODULE_SYNC_IDX].enabled < NETDATA_THREAD_EBPF_STOPPING)
1079 - return;
1080 -
1081 - int i;
1082 - for (i = 0; local_syscalls[i].syscall != NULL; i++) {
1083 - if (!local_syscalls[i].enabled)
1084 - continue;
1085 -
1086 -#ifdef LIBBPF_MAJOR_VERSION
1087 - if (local_syscalls[i].sync_obj) {
1088 - sync_bpf__destroy(local_syscalls[i].sync_obj);
1089 - continue;
1090 - }
1091 -#endif
1092 - ebpf_unload_legacy_code(local_syscalls[i].objects, local_syscalls[i].probe_links);
1093 - }
1094 -}
1095 -
1096 -/**
1097 - * Close the collector gracefully
1098 - *
1099 - * @param sig is the signal number used to close the collector
1100 - */
1101 -void ebpf_stop_threads(int sig)
1102 -{
1103 - UNUSED(sig);
1104 - static int only_one = 0;
1105 -
1106 - // Child thread should be closed by itself.
1107 - netdata_mutex_lock(&ebpf_exit_cleanup);
1108 - if (main_thread_id != gettid_cached() || only_one) {
1109 - netdata_mutex_unlock(&ebpf_exit_cleanup);
1110 - return;
1111 - }
1112 - only_one = 1;
1113 - int i;
1114 - for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
1115 - if (ebpf_modules[i].enabled < NETDATA_THREAD_EBPF_STOPPING) {
1116 - nd_thread_signal_cancel(ebpf_modules[i].thread->thread);
1117 -#ifdef NETDATA_DEV_MODE
1118 - netdata_log_info("Sending cancel for thread %s", ebpf_modules[i].info.thread_name);
1119 -#endif
1120 - }
1121 - }
1122 - netdata_mutex_unlock(&ebpf_exit_cleanup);
1123 -
1124 - for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
1125 - if (ebpf_threads[i].thread)
1126 - nd_thread_join(ebpf_threads[i].thread);
1127 - }
1128 -
1129 - __atomic_store_n(&ebpf_plugin_exit, true, __ATOMIC_RELEASE);
1130 -
1131 - netdata_mutex_lock(&mutex_cgroup_shm);
1132 - nd_thread_signal_cancel(cgroup_integration_thread.thread);
1133 -#ifdef NETDATA_DEV_MODE
1134 - netdata_log_info("Sending cancel for thread %s", cgroup_integration_thread.name);
1135 -#endif
1136 - netdata_mutex_unlock(&mutex_cgroup_shm);
1137 -
1138 - ebpf_check_before2go();
1139 -
1140 - netdata_mutex_lock(&ebpf_exit_cleanup);
1141 - ebpf_unload_unique_maps();
1142 - ebpf_unload_filesystems();
1143 - ebpf_unload_sync();
1144 - netdata_mutex_unlock(&ebpf_exit_cleanup);
1145 -
1146 - ebpf_exit();
1147 -}
1148 -
1149 -/*****************************************************************
1150 - *
1151 - * FUNCTIONS TO CREATE CHARTS
1152 - *
1153 - *****************************************************************/
1154 -
1155 -/**
1156 - * Create apps for module
1157 - *
1158 - * Create apps chart that will be used with specific module
1159 - *
1160 - * @param em the module main structure.
1161 - * @param root a pointer for the targets.
1162 - */
1163 -static inline void ebpf_create_apps_for_module(ebpf_module_t *em, struct ebpf_target *root)
1164 -{
1165 - if (em->enabled < NETDATA_THREAD_EBPF_STOPPING && em->apps_charts && em->functions.apps_routine)
1166 - em->functions.apps_routine(em, root);
1167 -}
1168 -
1169 -/**
1170 - * Create apps charts
1171 - *
1172 - * Call ebpf_create_chart to create the charts on apps submenu.
1173 - *
1174 - * @param root a pointer for the targets.
1175 - */
1176 -static void ebpf_create_apps_charts(struct ebpf_target *root)
1177 -{
1178 - // if (unlikely(!ebpf_pids))
1179 - // return;
1180 -
1181 - struct ebpf_target *w;
1182 - int newly_added = 0;
1183 -
1184 - for (w = root; w; w = w->next) {
1185 - if (w->target)
1186 - continue;
1187 -
1188 - if (unlikely(w->processes && (debug_enabled || w->debug_enabled))) {
1189 - struct ebpf_pid_on_target *pid_on_target;
1190 -
1191 - fprintf(
1192 - stderr,
1193 - "ebpf.plugin: target '%s' has aggregated %u process%s:",
1194 - w->name,
1195 - w->processes,
1196 - (w->processes == 1) ? "" : "es");
1197 -
1198 - for (pid_on_target = w->root_pid; pid_on_target; pid_on_target = pid_on_target->next) {
1199 - fprintf(stderr, " %d", pid_on_target->pid);
1200 - }
1201 -
1202 - fputc('\n', stderr);
1203 - }
1204 -
1205 - if (!w->exposed && w->processes) {
1206 - newly_added++;
1207 - w->exposed = 1;
1208 - if (debug_enabled || w->debug_enabled)
1209 - debug_log_int("%s just added - regenerating charts.", w->name);
1210 - }
1211 - }
1212 -
1213 - if (newly_added) {
1214 - int i;
1215 - for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
1216 - if (!(collect_pids & (1 << i)))
1217 - continue;
1218 -
1219 - ebpf_module_t *current = &ebpf_modules[i];
1220 - ebpf_create_apps_for_module(current, root);
1221 - }
1222 - }
1223 -}
1224 -
1225 -/**
1226 - * Get a value from a structure.
1227 - *
1228 - * @param basis it is the first address of the structure
1229 - * @param offset it is the offset of the data you want to access.
1230 - * @return
1231 - */
1232 -collected_number get_value_from_structure(char *basis, size_t offset)
1233 -{
1234 - collected_number *value = (collected_number *)(basis + offset);
1235 -
1236 - collected_number ret = (collected_number)llabs(*value);
1237 - // this reset is necessary to avoid keep a constant value while processing is not executing a task
1238 - *value = 0;
1239 -
1240 - return ret;
1241 -}
1242 -
1243 -/**
1244 - * Write set command on standard output
1245 - *
1246 - * @param dim the dimension name
1247 - * @param value the value for the dimension
1248 - */
1249 -void write_chart_dimension(char *dim, long long value)
1250 -{
1251 - printf("SET %s = %lld\n", dim, value);
1252 -}
1253 -
1254 -/**
1255 - * Call the necessary functions to create a chart.
1256 - *
1257 - * @param name the chart name
1258 - * @param family the chart family
1259 - * @param move the pointer with the values that will be published
1260 - * @param end the number of values that will be written on standard output
1261 - *
1262 - * @return It returns a variable that maps the charts that did not have zero values.
1263 - */
1264 -void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end)
1265 -{
1266 - ebpf_write_begin_chart(family, name, "");
1267 -
1268 - uint32_t i = 0;
1269 - while (move && i < end) {
1270 - write_chart_dimension(move->name, move->ncall);
1271 -
1272 - move = move->next;
1273 - i++;
1274 - }
1275 -
1276 - ebpf_write_end_chart();
1277 -}
1278 -
1279 -/**
1280 - * Call the necessary functions to create a chart.
1281 - *
1282 - * @param name the chart name
1283 - * @param family the chart family
1284 - * @param move the pointer with the values that will be published
1285 - * @param end the number of values that will be written on standard output
1286 - */
1287 -void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end)
1288 -{
1289 - ebpf_write_begin_chart(family, name, "");
1290 -
1291 - int i = 0;
1292 - while (move && i < end) {
1293 - write_chart_dimension(move->name, move->nerr);
1294 -
1295 - move = move->next;
1296 - i++;
1297 - }
1298 -
1299 - ebpf_write_end_chart();
1300 -}
1301 -
1302 -/**
1303 - * Write charts
1304 - *
1305 - * Write the current information to publish the charts.
1306 - *
1307 - * @param family chart family
1308 - * @param chart chart id
1309 - * @param dim dimension name
1310 - * @param v1 value.
1311 - */
1312 -void ebpf_one_dimension_write_charts(char *family, char *chart, char *dim, long long v1)
1313 -{
1314 - ebpf_write_begin_chart(family, chart, "");
1315 -
1316 - write_chart_dimension(dim, v1);
1317 -
1318 - ebpf_write_end_chart();
1319 -}
1320 -
1321 -/**
1322 - * Call the necessary functions to create a chart.
1323 - *
1324 - * @param chart the chart name
1325 - * @param family the chart family
1326 - * @param dwrite the dimension name
1327 - * @param vwrite the value for previous dimension
1328 - * @param dread the dimension name
1329 - * @param vread the value for previous dimension
1330 - *
1331 - * @return It returns a variable that maps the charts that did not have zero values.
1332 - */
1333 -void write_io_chart(char *chart, char *family, char *dwrite, long long vwrite, char *dread, long long vread)
1334 -{
1335 - ebpf_write_begin_chart(family, chart, "");
1336 -
1337 - write_chart_dimension(dwrite, vwrite);
1338 - write_chart_dimension(dread, vread);
1339 -
1340 - ebpf_write_end_chart();
1341 -}
1342 -
1343 -/**
1344 - * Write chart cmd on standard output
1345 - *
1346 - * @param type chart type
1347 - * @param id chart id (the apps group name).
1348 - * @param suffix suffix to differentiate charts
1349 - * @param title chart title
1350 - * @param units units label
1351 - * @param family group name used to attach the chart on dashboard
1352 - * @param charttype chart type
1353 - * @param context chart context
1354 - * @param order chart order
1355 - * @param update_every update interval used by plugin
1356 - * @param module chart module name, this is the eBPF thread.
1357 - */
1358 -void ebpf_write_chart_cmd(
1359 - char *type,
1360 - char *id,
1361 - char *suffix,
1362 - char *title,
1363 - char *units,
1364 - char *family,
1365 - char *charttype,
1366 - char *context,
1367 - int order,
1368 - int update_every,
1369 - char *module)
1370 -{
1371 - printf(
1372 - "CHART %s.%s%s '' '%s' '%s' '%s' '%s' '%s' %d %d '' 'ebpf.plugin' '%s'\n",
1373 - type,
1374 - id,
1375 - suffix,
1376 - title,
1377 - units,
1378 - (family) ? family : "",
1379 - (context) ? context : "",
1380 - (charttype) ? charttype : "",
1381 - order,
1382 - update_every,
1383 - module);
1384 -}
1385 -
1386 -/**
1387 - * Write chart cmd on standard output
1388 - *
1389 - * @param type chart type
1390 - * @param id chart id
1391 - * @param suffix add suffix to obsolete charts.
1392 - * @param title chart title
1393 - * @param units units label
1394 - * @param family group name used to attach the chart on dashboard
1395 - * @param charttype chart type
1396 - * @param context chart context
1397 - * @param order chart order
1398 - * @param update_every value to overwrite the update frequency set by the server.
1399 - */
1400 -void ebpf_write_chart_obsolete(
1401 - char *type,
1402 - char *id,
1403 - char *suffix,
1404 - char *title,
1405 - char *units,
1406 - char *family,
1407 - char *charttype,
1408 - char *context,
1409 - int order,
1410 - int update_every)
1411 -{
1412 - printf(
1413 - "CHART %s.%s%s '' '%s' '%s' '%s' '%s' '%s' %d %d 'obsolete'\n",
1414 - type,
1415 - id,
1416 - suffix,
1417 - title,
1418 - units,
1419 - (family) ? family : "",
1420 - (context) ? context : "",
1421 - (charttype) ? charttype : "",
1422 - order,
1423 - update_every);
1424 -}
1425 -
1426 -/**
1427 - * Write the dimension command on standard output
1428 - *
1429 - * @param name the dimension name
1430 - * @param id the dimension id
1431 - * @param algo the dimension algorithm
1432 - */
1433 -void ebpf_write_global_dimension(char *name, char *id, char *algorithm)
1434 -{
1435 - printf("DIMENSION %s %s %s 1 1\n", name, id, algorithm);
1436 -}
1437 -
1438 -/**
1439 - * Call ebpf_write_global_dimension to create the dimensions for a specific chart
1440 - *
1441 - * @param ptr a pointer to a structure of the type netdata_publish_syscall_t
1442 - * @param end the number of dimensions for the structure ptr
1443 - */
1444 -void ebpf_create_global_dimension(void *ptr, int end)
1445 -{
1446 - netdata_publish_syscall_t *move = ptr;
1447 -
1448 - int i = 0;
1449 - while (move && i < end) {
1450 - ebpf_write_global_dimension(move->name, move->dimension, move->algorithm);
1451 -
1452 - move = move->next;
1453 - i++;
1454 - }
1455 -}
1456 -
1457 -/**
1458 - * Call write_chart_cmd to create the charts
1459 - *
1460 - * @param type chart type
1461 - * @param id chart id
1462 - * @param title chart title
1463 - * @param units axis label
1464 - * @param family group name used to attach the chart on dashboard
1465 - * @param context chart context
1466 - * @param charttype chart type
1467 - * @param order order number of the specified chart
1468 - * @param ncd a pointer to a function called to create dimensions
1469 - * @param move a pointer for a structure that has the dimensions
1470 - * @param end number of dimensions for the chart created
1471 - * @param update_every update interval used with chart.
1472 - * @param module chart module name, this is the eBPF thread.
1473 - */
1474 -void ebpf_create_chart(
1475 - char *type,
1476 - char *id,
1477 - char *title,
1478 - char *units,
1479 - char *family,
1480 - char *context,
1481 - char *charttype,
1482 - int order,
1483 - void (*ncd)(void *, int),
1484 - void *move,
1485 - int end,
1486 - int update_every,
1487 - char *module)
1488 -{
1489 - ebpf_write_chart_cmd(type, id, "", title, units, family, charttype, context, order, update_every, module);
1490 -
1491 - if (ncd) {
1492 - ncd(move, end);
1493 - }
1494 -}
1495 -
1496 -/**
1497 - * Call the necessary functions to create a name.
1498 - *
1499 - * @param family family name
1500 - * @param name chart name
1501 - * @param hist0 histogram values
1502 - * @param dimensions dimension values.
1503 - * @param end number of bins that will be sent to Netdata.
1504 - *
1505 - * @return It returns a variable that maps the charts that did not have zero values.
1506 - */
1507 -void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, char **dimensions, uint32_t end)
1508 -{
1509 - ebpf_write_begin_chart(family, name, "");
1510 -
1511 - uint32_t i;
1512 - for (i = 0; i < end; i++) {
1513 - write_chart_dimension(dimensions[i], (long long)hist[i]);
1514 - }
1515 -
1516 - ebpf_write_end_chart();
1517 -
1518 - fflush(stdout);
1519 -}
1520 -
1521 -/**
1522 - * ARAL Charts
1523 - *
1524 - * Add chart to monitor ARAL usage
1525 - * Caller must call this function with mutex locked.
1526 - *
1527 - * @param name the name used to create aral
1528 - * @param em a pointer to the structure with the default values.
1529 - */
1530 -int ebpf_statistic_create_aral_chart(char *name, ebpf_module_t *em)
1531 -{
1532 - static int priority = NETATA_EBPF_ORDER_STAT_ARAL_BEGIN;
1533 - char *mem = {NETDATA_EBPF_STAT_DIMENSION_MEMORY};
1534 - char *aral = {NETDATA_EBPF_STAT_DIMENSION_ARAL};
1535 -
1536 - snprintfz(em->memory_usage, NETDATA_EBPF_CHART_MEM_LENGTH - 1, "aral_%s_size", name);
1537 - snprintfz(em->memory_allocations, NETDATA_EBPF_CHART_MEM_LENGTH - 1, "aral_%s_alloc", name);
1538 -
1539 - ebpf_write_chart_cmd(
1540 - NETDATA_MONITORING_FAMILY,
1541 - em->memory_usage,
1542 - "",
1543 - "Bytes allocated for ARAL.",
1544 - "bytes",
1545 - NETDATA_EBPF_FAMILY,
1546 - NETDATA_EBPF_CHART_TYPE_STACKED,
1547 - "netdata.ebpf_aral_stat_size",
1548 - priority++,
1549 - em->update_every,
1550 - NETDATA_EBPF_MODULE_NAME_PROCESS);
1551 -
1552 - ebpf_write_global_dimension(mem, mem, ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
1553 -
1554 - ebpf_write_chart_cmd(
1555 - NETDATA_MONITORING_FAMILY,
1556 - em->memory_allocations,
1557 - "",
1558 - "Calls to allocate memory.",
1559 - "calls",
1560 - NETDATA_EBPF_FAMILY,
1561 - NETDATA_EBPF_CHART_TYPE_STACKED,
1562 - "netdata.ebpf_aral_stat_alloc",
1563 - priority++,
1564 - em->update_every,
1565 - NETDATA_EBPF_MODULE_NAME_PROCESS);
1566 -
1567 - ebpf_write_global_dimension(aral, aral, ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
1568 -
1569 - return priority - 2;
1570 -}
1571 -
1572 -/**
1573 - * ARAL Charts
1574 - *
1575 - * Add chart to monitor ARAL usage
1576 - * Caller must call this function with mutex locked.
1577 - *
1578 - * @param em a pointer to the structure with the default values.
1579 - * @param prio the initial priority used to disable charts.
1580 - */
1581 -void ebpf_statistic_obsolete_aral_chart(ebpf_module_t *em, int prio)
1582 -{
1583 - ebpf_write_chart_obsolete(
1584 - NETDATA_MONITORING_FAMILY,
1585 - em->memory_allocations,
1586 - "",
1587 - "Calls to allocate memory.",
1588 - "calls",
1589 - NETDATA_EBPF_FAMILY,
1590 - NETDATA_EBPF_CHART_TYPE_STACKED,
1591 - "netdata.ebpf_aral_stat_alloc",
1592 - prio++,
1593 - em->update_every);
1594 -
1595 - ebpf_write_chart_obsolete(
1596 - NETDATA_MONITORING_FAMILY,
1597 - em->memory_allocations,
1598 - "",
1599 - "Calls to allocate memory.",
1600 - "calls",
1601 - NETDATA_EBPF_FAMILY,
1602 - NETDATA_EBPF_CHART_TYPE_STACKED,
1603 - "netdata.ebpf_aral_stat_alloc",
1604 - prio++,
1605 - em->update_every);
1606 -}
1607 -
1608 -/**
1609 - * Send data from aral chart
1610 - *
1611 - * Send data for eBPF plugin
1612 - *
1613 - * @param memory a pointer to the allocated address
1614 - * @param em a pointer to the structure with the default values.
1615 - */
1616 -void ebpf_send_data_aral_chart(ARAL *memory, ebpf_module_t *em)
1617 -{
1618 - char *mem = {NETDATA_EBPF_STAT_DIMENSION_MEMORY};
1619 - char *aral = {NETDATA_EBPF_STAT_DIMENSION_ARAL};
1620 -
1621 - struct aral_statistics *stats = aral_get_statistics(memory);
1622 -
1623 - ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, em->memory_usage, "");
1624 - write_chart_dimension(mem, (long long)stats->structures.allocated_bytes);
1625 - ebpf_write_end_chart();
1626 -
1627 - ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, em->memory_allocations, "");
1628 - write_chart_dimension(aral, (long long)stats->structures.allocations);
1629 - ebpf_write_end_chart();
1630 -}
1631 -
1632 -/*****************************************************************
1633 - *
1634 - * FUNCTIONS TO READ GLOBAL HASH TABLES
1635 - *
1636 - *****************************************************************/
1637 -
1638 -/**
1639 - * Read Global Table Stats
1640 - *
1641 - * Read data from specified table (map_fd) using array allocated inside thread(values) and storing
1642 - * them in stats vector starting from the first position.
1643 - *
1644 - * For PID tables is recommended to use a function to parse the specific data.
1645 - *
1646 - * @param stats vector used to store data
1647 - * @param values helper to read data from hash tables.
1648 - * @param map_fd table that has data
1649 - * @param maps_per_core Is necessary to read data from all cores?
1650 - * @param begin initial value to query hash table
1651 - * @param end last value that will not be used.
1652 - */
1653 -void ebpf_read_global_table_stats(
1654 - netdata_idx_t *stats,
1655 - netdata_idx_t *values,
1656 - int map_fd,
1657 - int maps_per_core,
1658 - uint32_t begin,
1659 - uint32_t end)
1660 -{
1661 - uint32_t idx, order;
1662 -
1663 - for (idx = begin, order = 0; idx < end; idx++, order++) {
1664 - if (!bpf_map_lookup_elem(map_fd, &idx, values)) {
1665 - int i;
1666 - int before = (maps_per_core) ? ebpf_nprocs : 1;
1667 - netdata_idx_t total = 0;
1668 - for (i = 0; i < before; i++)
1669 - total += values[i];
1670 -
1671 - stats[order] = total;
1672 - }
1673 - }
1674 -}
1675 -
1676 -/*****************************************************************
1677 - *
1678 - * FUNCTIONS USED WITH SOCKET
1679 - *
1680 - *****************************************************************/
1681 -
1682 -/**
1683 - * Netmask
1684 - *
1685 - * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
1686 - *
1687 - * @param prefix create the netmask based in the CIDR value.
1688 - *
1689 - * @return
1690 - */
1691 -static inline in_addr_t ebpf_netmask(int prefix)
1692 -{
1693 - if (prefix == 0)
1694 - return (~((in_addr_t)-1));
1695 - else
1696 - return (in_addr_t)(~((1 << (32 - prefix)) - 1));
1697 -}
1698 -
1699 -/**
1700 - * Broadcast
1701 - *
1702 - * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
1703 - *
1704 - * @param addr is the ip address
1705 - * @param prefix is the CIDR value.
1706 - *
1707 - * @return It returns the last address of the range
1708 - */
1709 -static inline in_addr_t ebpf_broadcast(in_addr_t addr, int prefix)
1710 -{
1711 - return (addr | ~ebpf_netmask(prefix));
1712 -}
1713 -
1714 -/**
1715 - * Network
1716 - *
1717 - * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
1718 - *
1719 - * @param addr is the ip address
1720 - * @param prefix is the CIDR value.
1721 - *
1722 - * @return It returns the first address of the range.
1723 - */
1724 -static inline in_addr_t ebpf_ipv4_network(in_addr_t addr, int prefix)
1725 -{
1726 - return (addr & ebpf_netmask(prefix));
1727 -}
1728 -
1729 -/**
1730 - * Calculate ipv6 first address
1731 - *
1732 - * @param out the address to store the first address.
1733 - * @param in the address used to do the math.
1734 - * @param prefix number of bits used to calculate the address
1735 - */
1736 -static void get_ipv6_first_addr(union netdata_ip_t *out, union netdata_ip_t *in, uint64_t prefix)
1737 -{
1738 - uint64_t mask, tmp;
1739 - uint64_t ret[2];
1740 -
1741 - memcpy(ret, in->addr32, sizeof(union netdata_ip_t));
1742 -
1743 - if (prefix == 128) {
1744 - memcpy(out->addr32, in->addr32, sizeof(union netdata_ip_t));
1745 - return;
1746 - } else if (!prefix) {
1747 - ret[0] = ret[1] = 0;
1748 - memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1749 - return;
1750 - } else if (prefix <= 64) {
1751 - ret[1] = 0ULL;
1752 -
1753 - tmp = be64toh(ret[0]);
1754 - mask = 0xFFFFFFFFFFFFFFFFULL << (64 - prefix);
1755 - tmp &= mask;
1756 - ret[0] = htobe64(tmp);
1757 - } else {
1758 - mask = 0xFFFFFFFFFFFFFFFFULL << (128 - prefix);
1759 - tmp = be64toh(ret[1]);
1760 - tmp &= mask;
1761 - ret[1] = htobe64(tmp);
1762 - }
1763 -
1764 - memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1765 -}
1766 -
1767 -/**
1768 - * Get IPV6 Last Address
1769 - *
1770 - * @param out the address to store the last address.
1771 - * @param in the address used to do the math.
1772 - * @param prefix number of bits used to calculate the address
1773 - */
1774 -static void get_ipv6_last_addr(union netdata_ip_t *out, union netdata_ip_t *in, uint64_t prefix)
1775 -{
1776 - uint64_t mask, tmp;
1777 - uint64_t ret[2];
1778 - memcpy(ret, in->addr32, sizeof(union netdata_ip_t));
1779 -
1780 - if (prefix == 128) {
1781 - memcpy(out->addr32, in->addr32, sizeof(union netdata_ip_t));
1782 - return;
1783 - } else if (!prefix) {
1784 - ret[0] = ret[1] = 0xFFFFFFFFFFFFFFFF;
1785 - memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1786 - return;
1787 - } else if (prefix <= 64) {
1788 - ret[1] = 0xFFFFFFFFFFFFFFFFULL;
1789 -
1790 - tmp = be64toh(ret[0]);
1791 - mask = 0xFFFFFFFFFFFFFFFFULL << (64 - prefix);
1792 - tmp |= ~mask;
1793 - ret[0] = htobe64(tmp);
1794 - } else {
1795 - mask = 0xFFFFFFFFFFFFFFFFULL << (128 - prefix);
1796 - tmp = be64toh(ret[1]);
1797 - tmp |= ~mask;
1798 - ret[1] = htobe64(tmp);
1799 - }
1800 -
1801 - memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
1802 -}
1803 -
1804 -/**
1805 - * IP to network long
1806 - *
1807 - * @param dst the vector to store the result
1808 - * @param ip the source ip given by our users.
1809 - * @param domain the ip domain (IPV4 or IPV6)
1810 - * @param source the original string
1811 - *
1812 - * @return it returns 0 on success and -1 otherwise.
1813 - */
1814 -static inline int ebpf_ip2nl(uint8_t *dst, const char *ip, int domain, char *source)
1815 -{
1816 - if (inet_pton(domain, ip, dst) <= 0) {
1817 - netdata_log_error("The address specified (%s) is invalid ", source);
1818 - return -1;
1819 - }
1820 -
1821 - return 0;
1822 -}
1823 -
1824 -/**
1825 - * Clean port Structure
1826 - *
1827 - * Clean the allocated list.
1828 - *
1829 - * @param clean the list that will be cleaned
1830 - */
1831 -void ebpf_clean_port_structure(ebpf_network_viewer_port_list_t **clean)
1832 -{
1833 - ebpf_network_viewer_port_list_t *move = *clean;
1834 - while (move) {
1835 - ebpf_network_viewer_port_list_t *next = move->next;
1836 - freez(move->value);
1837 - freez(move);
1838 -
1839 - move = next;
1840 - }
1841 - *clean = NULL;
1842 -}
1843 -
1844 -/**
1845 - * Clean IP structure
1846 - *
1847 - * Clean the allocated list.
1848 - *
1849 - * @param clean the list that will be cleaned
1850 - */
1851 -void ebpf_clean_ip_structure(ebpf_network_viewer_ip_list_t **clean)
1852 -{
1853 - ebpf_network_viewer_ip_list_t *move = *clean;
1854 - while (move) {
1855 - ebpf_network_viewer_ip_list_t *next = move->next;
1856 - freez(move->value);
1857 - freez(move);
1858 -
1859 - move = next;
1860 - }
1861 - *clean = NULL;
1862 -}
1863 -
1864 -/**
1865 - * Parse IP List
1866 - *
1867 - * Parse IP list and link it.
1868 - *
1869 - * @param out a pointer to store the link list
1870 - * @param ip the value given as parameter
1871 - */
1872 -static void ebpf_parse_ip_list_unsafe(void **out, const char *ip)
1873 -{
1874 - ebpf_network_viewer_ip_list_t **list = (ebpf_network_viewer_ip_list_t **)out;
1875 -
1876 - char *ipdup = strdupz(ip);
1877 - union netdata_ip_t first = {};
1878 - union netdata_ip_t last = {};
1879 - const char *is_ipv6;
1880 - if (*ip == '*' && *(ip + 1) == '\0') {
1881 - memset(first.addr8, 0, sizeof(first.addr8));
1882 - memset(last.addr8, 0xFF, sizeof(last.addr8));
1883 -
1884 - is_ipv6 = ip;
1885 -
1886 - ebpf_clean_ip_structure(list);
1887 - goto storethisip;
1888 - }
1889 -
1890 - char *enddup = strdupz(ip);
1891 - char *end = enddup;
1892 - // Move while I cannot find a separator
1893 - while (*end && *end != '/' && *end != '-')
1894 - end++;
1895 -
1896 - // We will use only the classic IPV6 for while, but we could consider the base 85 in a near future
1897 - // https://tools.ietf.org/html/rfc1924
1898 - is_ipv6 = strchr(ip, ':');
1899 -
1900 - int select;
1901 - if (*end && !is_ipv6) { // IPV4 range
1902 - select = (*end == '/') ? 0 : 1;
1903 - *end++ = '\0';
1904 - if (*end == '!') {
1905 - netdata_log_info("The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1906 - goto cleanipdup;
1907 - }
1908 -
1909 - if (!select) { // CIDR
1910 - select = ebpf_ip2nl(first.addr8, ip, AF_INET, ipdup);
1911 - if (select)
1912 - goto cleanipdup;
1913 -
1914 - select = (int)str2i(end);
1915 - if (select < NETDATA_MINIMUM_IPV4_CIDR || select > NETDATA_MAXIMUM_IPV4_CIDR) {
1916 - netdata_log_info("The specified CIDR %s is not valid, the IP %s will be ignored.", end, ip);
1917 - goto cleanipdup;
1918 - }
1919 -
1920 - last.addr32[0] = htonl(ebpf_broadcast(ntohl(first.addr32[0]), select));
1921 - // This was added to remove
1922 - // https://app.codacy.com/manual/netdata/netdata/pullRequest?prid=5810941&bid=19021977
1923 - UNUSED(last.addr32[0]);
1924 -
1925 - uint32_t ipv4_test = htonl(ebpf_ipv4_network(ntohl(first.addr32[0]), select));
1926 - if (first.addr32[0] != ipv4_test) {
1927 - first.addr32[0] = ipv4_test;
1928 - struct in_addr ipv4_convert;
1929 - ipv4_convert.s_addr = ipv4_test;
1930 - char ipv4_msg[INET_ADDRSTRLEN];
1931 - if (inet_ntop(AF_INET, &ipv4_convert, ipv4_msg, INET_ADDRSTRLEN))
1932 - netdata_log_info("The network value of CIDR %s was updated for %s .", ipdup, ipv4_msg);
1933 - }
1934 - } else { // Range
1935 - select = ebpf_ip2nl(first.addr8, ip, AF_INET, ipdup);
1936 - if (select)
1937 - goto cleanipdup;
1938 -
1939 - select = ebpf_ip2nl(last.addr8, end, AF_INET, ipdup);
1940 - if (select)
1941 - goto cleanipdup;
1942 - }
1943 -
1944 - if (htonl(first.addr32[0]) > htonl(last.addr32[0])) {
1945 - netdata_log_info(
1946 - "The specified range %s is invalid, the second address is smallest than the first, it will be ignored.",
1947 - ipdup);
1948 - goto cleanipdup;
1949 - }
1950 - } else if (is_ipv6) { // IPV6
1951 - if (!*end) { // Unique
1952 - select = ebpf_ip2nl(first.addr8, ip, AF_INET6, ipdup);
1953 - if (select)
1954 - goto cleanipdup;
1955 -
1956 - memcpy(last.addr8, first.addr8, sizeof(first.addr8));
1957 - } else if (*end == '-') {
1958 - *end++ = 0x00;
1959 - if (*end == '!') {
1960 - netdata_log_info(
1961 - "The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1962 - goto cleanipdup;
1963 - }
1964 -
1965 - select = ebpf_ip2nl(first.addr8, ip, AF_INET6, ipdup);
1966 - if (select)
1967 - goto cleanipdup;
1968 -
1969 - select = ebpf_ip2nl(last.addr8, end, AF_INET6, ipdup);
1970 - if (select)
1971 - goto cleanipdup;
1972 - } else { // CIDR
1973 - *end++ = 0x00;
1974 - if (*end == '!') {
1975 - netdata_log_info(
1976 - "The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1977 - goto cleanipdup;
1978 - }
1979 -
1980 - select = str2i(end);
1981 - if (select < 0 || select > 128) {
1982 - netdata_log_info("The CIDR %s is not valid, the address %s will be ignored.", end, ip);
1983 - goto cleanipdup;
1984 - }
1985 -
1986 - uint64_t prefix = (uint64_t)select;
1987 - select = ebpf_ip2nl(first.addr8, ip, AF_INET6, ipdup);
1988 - if (select)
1989 - goto cleanipdup;
1990 -
1991 - get_ipv6_last_addr(&last, &first, prefix);
1992 -
1993 - union netdata_ip_t ipv6_test;
1994 - get_ipv6_first_addr(&ipv6_test, &first, prefix);
1995 -
1996 - if (memcmp(first.addr8, ipv6_test.addr8, sizeof(union netdata_ip_t)) != 0) {
1997 - memcpy(first.addr8, ipv6_test.addr8, sizeof(union netdata_ip_t));
1998 -
1999 - struct in6_addr ipv6_convert;
2000 - memcpy(ipv6_convert.s6_addr, ipv6_test.addr8, sizeof(union netdata_ip_t));
2001 -
2002 - char ipv6_msg[INET6_ADDRSTRLEN];
2003 - if (inet_ntop(AF_INET6, &ipv6_convert, ipv6_msg, INET6_ADDRSTRLEN))
2004 - netdata_log_info("The network value of CIDR %s was updated for %s .", ipdup, ipv6_msg);
2005 - }
2006 - }
2007 -
2008 - if ((be64toh(*(uint64_t *)&first.addr64[1]) > be64toh(*(uint64_t *)&last.addr64[1]) &&
2009 - !memcmp(first.addr64, last.addr64, sizeof(uint64_t))) ||
2010 - (be64toh(*(uint64_t *)&first.addr64) > be64toh(*(uint64_t *)&last.addr64))) {
2011 - netdata_log_info(
2012 - "The specified range %s is invalid, the second address is smallest than the first, it will be ignored.",
2013 - ipdup);
2014 - goto cleanipdup;
2015 - }
2016 - } else { // Unique ip
2017 - select = ebpf_ip2nl(first.addr8, ip, AF_INET, ipdup);
2018 - if (select)
2019 - goto cleanipdup;
2020 -
2021 - memcpy(last.addr8, first.addr8, sizeof(first.addr8));
2022 - }
2023 -
2024 - ebpf_network_viewer_ip_list_t *store;
2025 -
2026 -storethisip:
2027 - store = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
2028 - store->value = ipdup;
2029 - store->hash = simple_hash(ipdup);
2030 - store->ver = (uint8_t)(!is_ipv6) ? AF_INET : AF_INET6;
2031 - memcpy(store->first.addr8, first.addr8, sizeof(first.addr8));
2032 - memcpy(store->last.addr8, last.addr8, sizeof(last.addr8));
2033 -
2034 - ebpf_fill_ip_list_unsafe(list, store, "socket");
2035 - return;
2036 -
2037 -cleanipdup:
2038 - freez(ipdup);
2039 - freez(enddup);
2040 -}
2041 -
2042 -/**
2043 - * Parse IP Range
2044 - *
2045 - * Parse the IP ranges given and create Network Viewer IP Structure
2046 - *
2047 - * @param ptr is a pointer with the text to parse.
2048 - */
2049 -void ebpf_parse_ips_unsafe(const char *ptr)
2050 -{
2051 - // No value
2052 - if (unlikely(!ptr))
2053 - return;
2054 -
2055 - while (likely(ptr)) {
2056 - // Move forward until next valid character
2057 - while (isspace(*ptr))
2058 - ptr++;
2059 -
2060 - // No valid value found
2061 - if (unlikely(!*ptr))
2062 - return;
2063 -
2064 - // Find space that ends the list
2065 - char *end = strchr(ptr, ' ');
2066 - if (end) {
2067 - *end++ = '\0';
2068 - }
2069 -
2070 - int neg = 0;
2071 - if (*ptr == '!') {
2072 - neg++;
2073 - ptr++;
2074 - }
2075 -
2076 - if (isascii(*ptr)) { // Parse port
2077 - ebpf_parse_ip_list_unsafe(
2078 - (!neg) ? (void **)&network_viewer_opt.included_ips : (void **)&network_viewer_opt.excluded_ips, ptr);
2079 - }
2080 -
2081 - ptr = end;
2082 - }
2083 -}
2084 -
2085 -/**
2086 - * Fill Port list
2087 - *
2088 - * @param out a pointer to the link list.
2089 - * @param in the structure that will be linked.
2090 - */
2091 -static inline void fill_port_list(ebpf_network_viewer_port_list_t **out, ebpf_network_viewer_port_list_t *in)
2092 -{
2093 - if (likely(*out)) {
2094 - ebpf_network_viewer_port_list_t *move = *out, *store = *out;
2095 - uint16_t first = ntohs(in->first);
2096 - uint16_t last = ntohs(in->last);
2097 - while (move) {
2098 - uint16_t cmp_first = ntohs(move->first);
2099 - uint16_t cmp_last = ntohs(move->last);
2100 - if (cmp_first <= first && first <= cmp_last && cmp_first <= last && last <= cmp_last) {
2101 - netdata_log_info(
2102 - "The range/value (%u, %u) is inside the range/value (%u, %u) already inserted, it will be ignored.",
2103 - first,
2104 - last,
2105 - cmp_first,
2106 - cmp_last);
2107 - freez(in->value);
2108 - freez(in);
2109 - return;
2110 - } else if (first <= cmp_first && cmp_first <= last && first <= cmp_last && cmp_last <= last) {
2111 - netdata_log_info(
2112 - "The range (%u, %u) is bigger than previous range (%u, %u) already inserted, the previous will be ignored.",
2113 - first,
2114 - last,
2115 - cmp_first,
2116 - cmp_last);
2117 - freez(move->value);
2118 - move->value = in->value;
2119 - move->first = in->first;
2120 - move->last = in->last;
2121 - freez(in);
2122 - return;
2123 - }
2124 -
2125 - store = move;
2126 - move = move->next;
2127 - }
2128 -
2129 - store->next = in;
2130 - } else {
2131 - *out = in;
2132 - }
2133 -
2134 -#ifdef NETDATA_INTERNAL_CHECKS
2135 - netdata_log_info(
2136 - "Adding values %s( %u, %u) to %s port list used on network viewer",
2137 - in->value,
2138 - in->first,
2139 - in->last,
2140 - (*out == network_viewer_opt.included_port) ? "included" : "excluded");
2141 -#endif
2142 -}
2143 -
2144 -/**
2145 - * Parse Service List
2146 - *
2147 - * @param out a pointer to store the link list
2148 - * @param service the service used to create the structure that will be linked.
2149 - */
2150 -static void ebpf_parse_service_list(void **out, const char *service)
2151 -{
2152 - ebpf_network_viewer_port_list_t **list = (ebpf_network_viewer_port_list_t **)out;
2153 - struct servent *serv = getservbyname((const char *)service, "tcp");
2154 - if (!serv)
2155 - serv = getservbyname((const char *)service, "udp");
2156 -
2157 - if (!serv) {
2158 - netdata_log_info("Cannot resolve the service '%s' with protocols TCP and UDP, it will be ignored", service);
2159 - return;
2160 - }
2161 -
2162 - ebpf_network_viewer_port_list_t *w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
2163 - w->value = strdupz(service);
2164 - w->hash = simple_hash(service);
2165 -
2166 - w->first = w->last = (uint16_t)serv->s_port;
2167 -
2168 - fill_port_list(list, w);
2169 -}
2170 -
2171 -/**
2172 - * Parse port list
2173 - *
2174 - * Parse an allocated port list with the range given
2175 - *
2176 - * @param out a pointer to store the link list
2177 - * @param range the informed range for the user.
2178 - */
2179 -static void ebpf_parse_port_list(void **out, const char *range_param)
2180 -{
2181 - char range[strlen(range_param) + 1];
2182 - strncpyz(range, range_param, strlen(range_param));
2183 -
2184 - int first, last;
2185 - ebpf_network_viewer_port_list_t **list = (ebpf_network_viewer_port_list_t **)out;
2186 -
2187 - char *copied = strdupz(range);
2188 - if (*range == '*' && *(range + 1) == '\0') {
2189 - first = 1;
2190 - last = 65535;
2191 -
2192 - ebpf_clean_port_structure(list);
2193 - goto fillenvpl;
2194 - }
2195 -
2196 - char *end = range;
2197 - //Move while I cannot find a separator
2198 - while (*end && *end != ':' && *end != '-')
2199 - end++;
2200 -
2201 - //It has a range
2202 - if (likely(*end)) {
2203 - *end++ = '\0';
2204 - if (*end == '!') {
2205 - netdata_log_info(
2206 - "The exclusion cannot be in the second part of the range, the range %s will be ignored.", copied);
2207 - freez(copied);
2208 - return;
2209 - }
2210 - last = str2i((const char *)end);
2211 - } else {
2212 - last = 0;
2213 - }
2214 -
2215 - first = str2i((const char *)range);
2216 - if (first < NETDATA_MINIMUM_PORT_VALUE || first > NETDATA_MAXIMUM_PORT_VALUE) {
2217 - netdata_log_info("The first port %d of the range \"%s\" is invalid and it will be ignored!", first, copied);
2218 - freez(copied);
2219 - return;
2220 - }
2221 -
2222 - if (!last)
2223 - last = first;
2224 -
2225 - if (last < NETDATA_MINIMUM_PORT_VALUE || last > NETDATA_MAXIMUM_PORT_VALUE) {
2226 - netdata_log_info(
2227 - "The second port %d of the range \"%s\" is invalid and the whole range will be ignored!", last, copied);
2228 - freez(copied);
2229 - return;
2230 - }
2231 -
2232 - if (first > last) {
2233 - netdata_log_info(
2234 - "The specified order %s is wrong, the smallest value is always the first, it will be ignored!", copied);
2235 - freez(copied);
2236 - return;
2237 - }
2238 -
2239 - ebpf_network_viewer_port_list_t *w;
2240 -fillenvpl:
2241 - w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
2242 - w->value = copied;
2243 - w->hash = simple_hash(copied);
2244 - w->first = (uint16_t)first;
2245 - w->last = (uint16_t)last;
2246 - w->cmp_first = (uint16_t)first;
2247 - w->cmp_last = (uint16_t)last;
2248 -
2249 - fill_port_list(list, w);
2250 -}
2251 -
2252 -/**
2253 - * Parse Port Range
2254 - *
2255 - * Parse the port ranges given and create Network Viewer Port Structure
2256 - *
2257 - * @param ptr is a pointer with the text to parse.
2258 - */
2259 -void ebpf_parse_ports(const char *ptr)
2260 -{
2261 - // No value
2262 - if (unlikely(!ptr))
2263 - return;
2264 -
2265 - while (likely(ptr)) {
2266 - // Move forward until next valid character
2267 - while (isspace(*ptr))
2268 - ptr++;
2269 -
2270 - // No valid value found
2271 - if (unlikely(!*ptr))
2272 - return;
2273 -
2274 - // Find space that ends the list
2275 - char *end = strchr(ptr, ' ');
2276 - if (end) {
2277 - *end++ = '\0';
2278 - }
2279 -
2280 - int neg = 0;
2281 - if (*ptr == '!') {
2282 - neg++;
2283 - ptr++;
2284 - }
2285 -
2286 - if (isdigit(*ptr)) { // Parse port
2287 - ebpf_parse_port_list(
2288 - (!neg) ? (void **)&network_viewer_opt.included_port : (void **)&network_viewer_opt.excluded_port, ptr);
2289 - } else if (isalpha(*ptr)) { // Parse service
2290 - ebpf_parse_service_list(
2291 - (!neg) ? (void **)&network_viewer_opt.included_port : (void **)&network_viewer_opt.excluded_port, ptr);
2292 - } else if (*ptr == '*') { // All
2293 - ebpf_parse_port_list(
2294 - (!neg) ? (void **)&network_viewer_opt.included_port : (void **)&network_viewer_opt.excluded_port, ptr);
2295 - }
2296 -
2297 - ptr = end;
2298 - }
2299 -}
2300 -
2301 -/*****************************************************************
2302 - *
2303 - * FUNCTIONS TO DEFINE OPTIONS
2304 - *
2305 - *****************************************************************/
2306 -
2307 -/**
2308 - * Define labels used to generate charts
2309 - *
2310 - * @param is structure with information about number of calls made for a function.
2311 - * @param pio structure used to generate charts.
2312 - * @param dim a pointer for the dimensions name
2313 - * @param name a pointer for the tensor with the name of the functions.
2314 - * @param algorithm a vector with the algorithms used to make the charts
2315 - * @param end the number of elements in the previous 4 arguments.
2316 - */
2317 -void ebpf_global_labels(
2318 - netdata_syscall_stat_t *is,
2319 - netdata_publish_syscall_t *pio,
2320 - char **dim,
2321 - char **name,
2322 - int *algorithm,
2323 - int end)
2324 -{
2325 - int i;
2326 -
2327 - netdata_syscall_stat_t *prev = NULL;
2328 - netdata_publish_syscall_t *publish_prev = NULL;
2329 - for (i = 0; i < end; i++) {
2330 - if (prev) {
2331 - prev->next = &is[i];
2332 - }
2333 - prev = &is[i];
2334 -
2335 - pio[i].dimension = dim[i];
2336 - pio[i].name = name[i];
2337 - pio[i].algorithm = ebpf_algorithms[algorithm[i]];
2338 - if (publish_prev) {
2339 - publish_prev->next = &pio[i];
2340 - }
2341 - publish_prev = &pio[i];
2342 - }
2343 -}
2344 -
2345 -/**
2346 - * Define thread mode for all ebpf program.
2347 - *
2348 - * @param lmode the mode that will be used for them.
2349 - */
2350 -static inline void ebpf_set_thread_mode(netdata_run_mode_t lmode)
2351 -{
2352 - int i;
2353 - for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
2354 - ebpf_modules[i].mode = lmode;
2355 - }
2356 -}
2357 -
2358 -/**
2359 - * Enable specific charts selected by user.
2360 - *
2361 - * @param em the structure that will be changed
2362 - * @param disable_cgroup the status about the cgroups charts.
2363 - */
2364 -static inline void ebpf_enable_specific_chart(struct ebpf_module *em, int disable_cgroup)
2365 -{
2366 - em->enabled = NETDATA_THREAD_EBPF_RUNNING;
2367 -
2368 - if (!disable_cgroup) {
2369 - em->cgroup_charts = CONFIG_BOOLEAN_YES;
2370 - }
2371 -
2372 - em->global_charts = CONFIG_BOOLEAN_YES;
2373 -}
2374 -
2375 -/**
2376 - * Disable all Global charts
2377 - *
2378 - * Disable charts
2379 - */
2380 -static inline void disable_all_global_charts()
2381 -{
2382 - int i;
2383 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
2384 - ebpf_modules[i].enabled = NETDATA_THREAD_EBPF_NOT_RUNNING;
2385 - ebpf_modules[i].global_charts = 0;
2386 - }
2387 -}
2388 -
2389 -/**
2390 - * Enable the specified chart group
2391 - *
2392 - * @param idx the index of ebpf_modules that I am enabling
2393 - */
2394 -static inline void ebpf_enable_chart(int idx, int disable_cgroup)
2395 -{
2396 - int i;
2397 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
2398 - if (i == idx) {
2399 - ebpf_enable_specific_chart(&ebpf_modules[i], disable_cgroup);
2400 - break;
2401 - }
2402 - }
2403 -}
2404 -
2405 -/**
2406 - * Disable Cgroups
2407 - *
2408 - * Disable charts for apps loading only global charts.
2409 - */
2410 -static inline void ebpf_disable_cgroups()
2411 -{
2412 - int i;
2413 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
2414 - ebpf_modules[i].cgroup_charts = 0;
2415 - }
2416 -}
2417 -
2418 -/**
2419 - * Update Disabled Plugins
2420 - *
2421 - * This function calls ebpf_update_stats to update statistics for collector.
2422 - *
2423 - * @param em a pointer to `struct ebpf_module`
2424 - */
2425 -void ebpf_update_disabled_plugin_stats(ebpf_module_t *em)
2426 -{
2427 - netdata_mutex_lock(&lock);
2428 - ebpf_update_stats(&plugin_statistics, em);
2429 - netdata_mutex_unlock(&lock);
2430 -}
2431 -
2432 -/**
2433 - * Print help on standard error for user knows how to use the collector.
2434 - */
2435 -void ebpf_print_help()
2436 -{
2437 - fprintf(
2438 - stderr,
2439 - "\n"
2440 - " Netdata ebpf.plugin %s\n"
2441 - " Copyright 2018-2025 Netdata Inc.\n"
2442 - " Released under GNU General Public License v3 or later.\n"
2443 - "\n"
2444 - " This eBPF.plugin is a data collector plugin for netdata.\n"
2445 - "\n"
2446 - " This plugin only accepts long options with one or two dashes. The available command line options are:\n"
2447 - "\n"
2448 - " SECONDS Set the data collection frequency.\n"
2449 - "\n"
2450 - " [-]-help Show this help.\n"
2451 - "\n"
2452 - " [-]-version Show software version.\n"
2453 - "\n"
2454 - " [-]-global Disable charts per application and cgroup.\n"
2455 - "\n"
2456 - " [-]-all Enable all chart groups (global, apps, and cgroup), unless -g is also given.\n"
2457 - "\n"
2458 - " [-]-cachestat Enable charts related to process run time.\n"
2459 - "\n"
2460 - " [-]-dcstat Enable charts related to directory cache.\n"
2461 - "\n"
2462 - " [-]-disk Enable charts related to disk monitoring.\n"
2463 - "\n"
2464 - " [-]-filesystem Enable chart related to filesystem run time.\n"
2465 - "\n"
2466 - " [-]-hardirq Enable chart related to hard IRQ latency.\n"
2467 - "\n"
2468 - " [-]-mdflush Enable charts related to multi-device flush.\n"
2469 - "\n"
2470 - " [-]-mount Enable charts related to mount monitoring.\n"
2471 - "\n"
2472 - " [-]-net Enable network viewer charts.\n"
2473 - "\n"
2474 - " [-]-oomkill Enable chart related to OOM kill tracking.\n"
2475 - "\n"
2476 - " [-]-process Enable charts related to process run time.\n"
2477 - "\n"
2478 - " [-]-return Run the collector in return mode.\n"
2479 - "\n"
2480 - " [-]-shm Enable chart related to shared memory tracking.\n"
2481 - "\n"
2482 - " [-]-softirq Enable chart related to soft IRQ latency.\n"
2483 - "\n"
2484 - " [-]-sync Enable chart related to sync run time.\n"
2485 - "\n"
2486 - " [-]-swap Enable chart related to swap run time.\n"
2487 - "\n"
2488 - " [-]-vfs Enable chart related to vfs run time.\n"
2489 - "\n"
2490 - " [-]-legacy Load legacy eBPF programs.\n"
2491 - "\n"
2492 - " [-]-core Use CO-RE when available(Working in progress).\n"
2493 - "\n",
2494 - NETDATA_VERSION);
2495 -}
2496 -
2497 -/*****************************************************************
2498 - *
2499 - * TRACEPOINT MANAGEMENT FUNCTIONS
2500 - *
2501 - *****************************************************************/
2502 -
2503 -/**
2504 - * Enable a tracepoint.
2505 - *
2506 - * @return 0 on success, -1 on error.
2507 - */
2508 -int ebpf_enable_tracepoint(ebpf_tracepoint_t *tp)
2509 -{
2510 - int test = ebpf_is_tracepoint_enabled(tp->class, tp->event);
2511 -
2512 - // err?
2513 - if (test == -1) {
2514 - return -1;
2515 - }
2516 - // disabled?
2517 - else if (test == 0) {
2518 - // enable it then.
2519 - if (ebpf_enable_tracing_values(tp->class, tp->event)) {
2520 - return -1;
2521 - }
2522 - }
2523 -
2524 - // enabled now or already was.
2525 - tp->enabled = true;
2526 -
2527 - return 0;
2528 -}
2529 -
2530 -/**
2531 - * Disable a tracepoint if it's enabled.
2532 - *
2533 - * @return 0 on success, -1 on error.
2534 - */
2535 -int ebpf_disable_tracepoint(ebpf_tracepoint_t *tp)
2536 -{
2537 - int test = ebpf_is_tracepoint_enabled(tp->class, tp->event);
2538 -
2539 - // err?
2540 - if (test == -1) {
2541 - return -1;
2542 - }
2543 - // enabled?
2544 - else if (test == 1) {
2545 - // disable it then.
2546 - if (ebpf_disable_tracing_values(tp->class, tp->event)) {
2547 - return -1;
2548 - }
2549 - }
2550 -
2551 - // disable now or already was.
2552 - tp->enabled = false;
2553 -
2554 - return 0;
2555 -}
2556 -
2557 -/**
2558 - * Enable multiple tracepoints on a list of tracepoints which end when the
2559 - * class is NULL.
2560 - *
2561 - * @return the number of successful enables.
2562 - */
2563 -uint32_t ebpf_enable_tracepoints(ebpf_tracepoint_t *tps)
2564 -{
2565 - uint32_t cnt = 0;
2566 - for (int i = 0; tps[i].class != NULL; i++) {
2567 - if (ebpf_enable_tracepoint(&tps[i]) == -1) {
2568 - netdata_log_error("Failed to enable tracepoint %s:%s", tps[i].class, tps[i].event);
2569 - } else {
2570 - cnt += 1;
2571 - }
2572 - }
2573 - return cnt;
2574 -}
2575 -
2576 -/*****************************************************************
2577 - *
2578 - * AUXILIARY FUNCTIONS USED DURING INITIALIZATION
2579 - *
2580 - *****************************************************************/
2581 -
2582 -/**
2583 - * Is ip inside the range
2584 - *
2585 - * Check if the ip is inside a IP range
2586 - *
2587 - * @param rfirst the first ip address of the range
2588 - * @param rlast the last ip address of the range
2589 - * @param cmpfirst the first ip to compare
2590 - * @param cmplast the last ip to compare
2591 - * @param family the IP family
2592 - *
2593 - * @return It returns 1 if the IP is inside the range and 0 otherwise
2594 - */
2595 -static int ebpf_is_ip_inside_range(
2596 - union netdata_ip_t *rfirst,
2597 - union netdata_ip_t *rlast,
2598 - union netdata_ip_t *cmpfirst,
2599 - union netdata_ip_t *cmplast,
2600 - int family)
2601 -{
2602 - if (family == AF_INET) {
2603 - if ((rfirst->addr32[0] <= cmpfirst->addr32[0]) && (rlast->addr32[0] >= cmplast->addr32[0]))
2604 - return 1;
2605 - } else {
2606 - if (memcmp(rfirst->addr8, cmpfirst->addr8, sizeof(union netdata_ip_t)) <= 0 &&
2607 - memcmp(rlast->addr8, cmplast->addr8, sizeof(union netdata_ip_t)) >= 0) {
2608 - return 1;
2609 - }
2610 - }
2611 - return 0;
2612 -}
2613 -
2614 -/**
2615 - * Fill IP list
2616 - *
2617 - * @param out a pointer to the link list.
2618 - * @param in the structure that will be linked.
2619 - * @param table the modified table.
2620 - */
2621 -void ebpf_fill_ip_list_unsafe(
2622 - ebpf_network_viewer_ip_list_t **out,
2623 - ebpf_network_viewer_ip_list_t *in,
2624 - char *table __maybe_unused)
2625 -{
2626 - if (in->ver == AF_INET) { // It is simpler to compare using host order
2627 - in->first.addr32[0] = ntohl(in->first.addr32[0]);
2628 - in->last.addr32[0] = ntohl(in->last.addr32[0]);
2629 - }
2630 - if (likely(*out)) {
2631 - ebpf_network_viewer_ip_list_t *move = *out, *store = *out;
2632 - while (move) {
2633 - if (in->ver == move->ver &&
2634 - ebpf_is_ip_inside_range(&move->first, &move->last, &in->first, &in->last, in->ver)) {
2635 -#ifdef NETDATA_DEV_MODE
2636 - netdata_log_info(
2637 - "The range/value (%s) is inside the range/value (%s) already inserted, it will be ignored.",
2638 - in->value,
2639 - move->value);
2640 -#endif
2641 - freez(in->value);
2642 - freez(in);
2643 - return;
2644 - }
2645 - store = move;
2646 - move = move->next;
2647 - }
2648 -
2649 - store->next = in;
2650 - } else {
2651 - *out = in;
2652 - }
2653 -
2654 -#ifdef NETDATA_DEV_MODE
2655 - char first[256], last[512];
2656 - if (in->ver == AF_INET) {
2657 - netdata_log_info(
2658 - "Adding values %s: (%u - %u) to %s IP list \"%s\" used on network viewer",
2659 - in->value,
2660 - in->first.addr32[0],
2661 - in->last.addr32[0],
2662 - (*out == network_viewer_opt.included_ips) ? "included" : "excluded",
2663 - table);
2664 - } else {
2665 - if (inet_ntop(AF_INET6, in->first.addr8, first, INET6_ADDRSTRLEN) &&
2666 - inet_ntop(AF_INET6, in->last.addr8, last, INET6_ADDRSTRLEN))
2667 - netdata_log_info(
2668 - "Adding values %s - %s to %s IP list \"%s\" used on network viewer",
2669 - first,
2670 - last,
2671 - (*out == network_viewer_opt.included_ips) ? "included" : "excluded",
2672 - table);
2673 - }
2674 -#endif
2675 -}
2676 -
2677 -/**
2678 - * Link hostname
2679 - *
2680 - * @param out is the output link list
2681 - * @param in the hostname to add to list.
2682 - */
2683 -static void ebpf_link_hostname(ebpf_network_viewer_hostname_list_t **out, ebpf_network_viewer_hostname_list_t *in)
2684 -{
2685 - if (likely(*out)) {
2686 - ebpf_network_viewer_hostname_list_t *move = *out;
2687 - for (; move->next; move = move->next) {
2688 - if (move->hash == in->hash && !strcmp(move->value, in->value)) {
2689 - netdata_log_info("The hostname %s was already inserted, it will be ignored.", in->value);
2690 - freez(in->value);
2691 - simple_pattern_free(in->value_pattern);
2692 - freez(in);
2693 - return;
2694 - }
2695 - }
2696 -
2697 - move->next = in;
2698 - } else {
2699 - *out = in;
2700 - }
2701 -#ifdef NETDATA_INTERNAL_CHECKS
2702 - netdata_log_info(
2703 - "Adding value %s to %s hostname list used on network viewer",
2704 - in->value,
2705 - (*out == network_viewer_opt.included_hostnames) ? "included" : "excluded");
2706 -#endif
2707 -}
2708 -
2709 -/**
2710 - * Link Hostnames
2711 - *
2712 - * Parse the list of hostnames to create the link list.
2713 - * This is not associated with the IP, because simple patterns like *example* cannot be resolved to IP.
2714 - *
2715 - * @param out is the output link list
2716 - * @param parse is a pointer with the text to parser.
2717 - */
2718 -static void ebpf_link_hostnames(const char *parse)
2719 -{
2720 - // No value
2721 - if (unlikely(!parse))
2722 - return;
2723 -
2724 - while (likely(parse)) {
2725 - // Find the first valid value
2726 - while (isspace(*parse))
2727 - parse++;
2728 -
2729 - // No valid value found
2730 - if (unlikely(!*parse))
2731 - return;
2732 -
2733 - // Find space that ends the list
2734 - char *end = strchr(parse, ' ');
2735 - if (end) {
2736 - *end++ = '\0';
2737 - }
2738 -
2739 - int neg = 0;
2740 - if (*parse == '!') {
2741 - neg++;
2742 - parse++;
2743 - }
2744 -
2745 - ebpf_network_viewer_hostname_list_t *hostname = callocz(1, sizeof(ebpf_network_viewer_hostname_list_t));
2746 - hostname->value = strdupz(parse);
2747 - hostname->hash = simple_hash(parse);
2748 - hostname->value_pattern = simple_pattern_create(parse, NULL, SIMPLE_PATTERN_EXACT, true);
2749 -
2750 - ebpf_link_hostname(
2751 - (!neg) ? &network_viewer_opt.included_hostnames : &network_viewer_opt.excluded_hostnames, hostname);
2752 -
2753 - parse = end;
2754 - }
2755 -}
2756 -
2757 -/**
2758 - * Parse network viewer section
2759 - *
2760 - * @param cfg the configuration structure
2761 - */
2762 -void parse_network_viewer_section(struct config *cfg)
2763 -{
2764 - network_viewer_opt.hostname_resolution_enabled =
2765 - inicfg_get_boolean(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_RESOLVE_HOSTNAME, CONFIG_BOOLEAN_NO);
2766 -
2767 - network_viewer_opt.service_resolution_enabled =
2768 - inicfg_get_boolean(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_RESOLVE_SERVICE, CONFIG_BOOLEAN_YES);
2769 -
2770 - const char *value = inicfg_get(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_PORTS, NULL);
2771 - ebpf_parse_ports(value);
2772 -
2773 - if (network_viewer_opt.hostname_resolution_enabled) {
2774 - value = inicfg_get(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_HOSTNAMES, NULL);
2775 - ebpf_link_hostnames(value);
2776 - } else {
2777 - netdata_log_info("Name resolution is disabled, collector will not parse \"hostnames\" list.");
2778 - }
2779 -
2780 - value = inicfg_get(cfg, EBPF_NETWORK_VIEWER_SECTION, "ips", NULL);
2781 - //"ips", "!127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 fc00::/7 !::1/128");
2782 - ebpf_parse_ips_unsafe(value);
2783 -}
2784 -
2785 -/**
2786 - * Read Local Ports
2787 - *
2788 - * Parse /proc/net/{tcp,udp} and get the ports Linux is listening.
2789 - *
2790 - * @param filename the proc file to parse.
2791 - * @param proto is the magic number associated to the protocol file we are reading.
2792 - */
2793 -static void read_local_ports(char *filename, uint8_t proto)
2794 -{
2795 - procfile *ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
2796 - if (!ff)
2797 - return;
2798 -
2799 - ff = procfile_readall(ff);
2800 - if (!ff)
2801 - return;
2802 -
2803 - size_t lines = procfile_lines(ff), l;
2804 - netdata_passive_connection_t values = {.counter = 0, .tgid = 0, .pid = 0};
2805 - for (l = 0; l < lines; l++) {
2806 - size_t words = procfile_linewords(ff, l);
2807 - // This is header or end of file
2808 - if (unlikely(words < 14))
2809 - continue;
2810 -
2811 - // https://elixir.bootlin.com/linux/v5.7.8/source/include/net/tcp_states.h
2812 - // 0A = TCP_LISTEN
2813 - if (strcmp("0A", procfile_lineword(ff, l, 5)))
2814 - continue;
2815 -
2816 - // Read local port
2817 - uint16_t port = (uint16_t)strtol(procfile_lineword(ff, l, 2), NULL, 16);
2818 - update_listen_table(htons(port), proto, &values);
2819 - }
2820 -
2821 - procfile_close(ff);
2822 -}
2823 -
2824 -/**
2825 - * Read Local addresseses
2826 - *
2827 - * Read the local address from the interfaces.
2828 - */
2829 -void ebpf_read_local_addresses_unsafe()
2830 -{
2831 - struct ifaddrs *ifaddr, *ifa;
2832 - if (getifaddrs(&ifaddr) == -1) {
2833 - netdata_log_error(
2834 - "Cannot get the local IP addresses, it is no possible to do separation between inbound and outbound connections");
2835 - return;
2836 - }
2837 -
2838 - char *notext = {"No text representation"};
2839 - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
2840 - if (ifa->ifa_addr == NULL)
2841 - continue;
2842 -
2843 - if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6))
2844 - continue;
2845 -
2846 - ebpf_network_viewer_ip_list_t *w = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
2847 -
2848 - int family = ifa->ifa_addr->sa_family;
2849 - w->ver = (uint8_t)family;
2850 - char text[INET6_ADDRSTRLEN];
2851 - if (family == AF_INET) {
2852 - struct sockaddr_in *in = (struct sockaddr_in *)ifa->ifa_addr;
2853 -
2854 - w->first.addr32[0] = in->sin_addr.s_addr;
2855 - w->last.addr32[0] = in->sin_addr.s_addr;
2856 -
2857 - if (inet_ntop(AF_INET, w->first.addr8, text, INET_ADDRSTRLEN)) {
2858 - w->value = strdupz(text);
2859 - w->hash = simple_hash(text);
2860 - } else {
2861 - w->value = strdupz(notext);
2862 - w->hash = simple_hash(notext);
2863 - }
2864 - } else {
2865 - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)ifa->ifa_addr;
2866 -
2867 - memcpy(w->first.addr8, (void *)&in6->sin6_addr, sizeof(struct in6_addr));
2868 - memcpy(w->last.addr8, (void *)&in6->sin6_addr, sizeof(struct in6_addr));
2869 -
2870 - if (inet_ntop(AF_INET6, w->first.addr8, text, INET_ADDRSTRLEN)) {
2871 - w->value = strdupz(text);
2872 - w->hash = simple_hash(text);
2873 - } else {
2874 - w->value = strdupz(notext);
2875 - w->hash = simple_hash(notext);
2876 - }
2877 - }
2878 -
2879 - ebpf_fill_ip_list_unsafe(
2880 - (family == AF_INET) ? &network_viewer_opt.ipv4_local_ip : &network_viewer_opt.ipv6_local_ip, w, "selector");
2881 - }
2882 -
2883 - freeifaddrs(ifaddr);
2884 -}
2885 -
2886 -/**
2887 - * Start Pthread Variable
2888 - *
2889 - * This function starts all
2890 - */
2891 -static void ebpf_mutex_initialize()
2892 -{
2893 - netdata_mutex_init(&lock);
2894 - netdata_mutex_init(&ebpf_exit_cleanup);
2895 - netdata_mutex_init(&collect_data_mutex);
2896 - netdata_mutex_init(&mutex_cgroup_shm);
2897 - rw_spinlock_init(&ebpf_judy_pid.index.rw_spinlock);
2898 -}
2899 -
2900 -/**
2901 - * Allocate the vectors used for all threads.
2902 - */
2903 -static void ebpf_allocate_common_vectors()
2904 -{
2905 - ebpf_judy_pid.pid_table =
2906 - ebpf_allocate_pid_aral(NETDATA_EBPF_PID_SOCKET_ARAL_TABLE_NAME, sizeof(netdata_ebpf_judy_pid_stats_t));
2907 - // ebpf_pids = callocz((size_t)pid_max, sizeof(ebpf_pid_data_t));
2908 - ebpf_aral_init();
2909 -}
897 +#endif
898 +const char *btf_path = NULL;
899
2911 -/**
2912 - * Define how to load the ebpf programs
900 +/*****************************************************************
901 *
2914 - * @param ptr the option given by users
2915 - */
2916 -static inline void ebpf_how_to_load(const char *ptr)
2917 -{
2918 - if (!strcasecmp(ptr, EBPF_CFG_LOAD_MODE_RETURN))
2919 - ebpf_set_thread_mode(MODE_RETURN);
2920 - else if (!strcasecmp(ptr, EBPF_CFG_LOAD_MODE_DEFAULT))
2921 - ebpf_set_thread_mode(MODE_ENTRY);
2922 - else
2923 - netdata_log_error("the option %s for \"ebpf load mode\" is not a valid option.", ptr);
2924 -}
902 + * FUNCTIONS USED TO MANIPULATE JUDY ARRAY
903 + *
904 + *****************************************************************/
905
906 /**
2927 - * Define whether we should have charts for apps
907 + * Hashtable insert unsafe
908 + *
909 + * Find or create a value associated to the index
910 + *
911 + * @return The lsocket = 0 when new item added to the array otherwise the existing item value is returned in *lsocket
912 + * we return a pointer to a pointer, so that the caller can put anything needed at the value of the index.
913 + * The pointer to pointer we return has to be used before any other operation that may change the index (insert/delete).
914 *
2929 - * @param lmode the mode that will be used for them.
915 */
2931 -static inline void ebpf_set_apps_mode(netdata_apps_integration_flags_t value)
916 +void **ebpf_judy_insert_unsafe(PPvoid_t arr, Word_t key)
917 {
2933 - int i;
2934 - for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
2935 - ebpf_modules[i].apps_charts = value;
918 + JError_t J_Error;
919 + Pvoid_t *idx = JudyLIns(arr, key, &J_Error);
920 + if (unlikely(idx == PJERR)) {
921 + netdata_log_error(
922 + "Cannot add PID to JudyL, JU_ERRNO_* == %u, ID == %d", JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
923 }
924 +
925 + return idx;
926 }
927
928 /**
2940 - * Update interval
929 + * Get PID from judy
930 *
2942 - * Update default interval with value from user
931 + * Get a pointer for the `pid` from judy_array;
932 *
2944 - * @param update_every value to overwrite the update frequency set by the server.
933 + * @param judy_array a judy array where PID is the primary key
934 + * @param pid pid stored.
935 */
2946 -static void ebpf_update_interval(int update_every)
936 +netdata_ebpf_judy_pid_stats_t *ebpf_get_pid_from_judy_unsafe(PPvoid_t judy_array, uint32_t pid)
937 {
2948 - int i;
938 + netdata_ebpf_judy_pid_stats_t **pid_pptr =
939 + (netdata_ebpf_judy_pid_stats_t **)ebpf_judy_insert_unsafe(judy_array, pid);
940 + netdata_ebpf_judy_pid_stats_t *pid_ptr = *pid_pptr;
941 + if (likely(*pid_pptr == NULL)) {
942 + *pid_pptr = aral_mallocz(ebpf_judy_pid.pid_table);
943 + if (unlikely(*pid_pptr == NULL)) {
944 + netdata_log_error("Cannot allocate memory for PID %u", pid);
945 + return NULL;
946 + }
947
2950 - int value = (int)inicfg_get_number(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_UPDATE_EVERY, update_every);
948 + pid_ptr = *pid_pptr;
949
2952 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
2953 - ebpf_modules[i].update_every = value;
950 + pid_ptr->cmdline = NULL;
951 + pid_ptr->socket_stats.JudyLArray = NULL;
952 + rw_spinlock_init(&pid_ptr->socket_stats.rw_spinlock);
953 }
2955 -}
954
2957 -/**
2958 - * Update PID table size
2959 - *
2960 - * Update default size with value from user
2961 - */
2962 -static void ebpf_update_table_size()
2963 -{
2964 - uint32_t value = (uint32_t)inicfg_get_number(
2965 - &collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, ND_EBPF_DEFAULT_PID_SIZE);
2966 - for (int i = 0; ebpf_modules[i].info.thread_name; i++) {
2967 - ebpf_modules[i].pid_map_size = value;
2968 - }
955 + return pid_ptr;
956 }
957
2971 -/**
2972 - * Update lifetime
958 +/*****************************************************************
959 *
2974 - * Update the period of time that specific thread will run
2975 - */
2976 -static void ebpf_update_lifetime()
2977 -{
2978 - uint32_t value =
2979 - (uint32_t)inicfg_get_number(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_LIFETIME, EBPF_DEFAULT_LIFETIME);
2980 -
2981 - for (int i = 0; ebpf_modules[i].info.thread_name; i++) {
2982 - ebpf_modules[i].lifetime = value;
2983 - }
2984 -}
960 + * FUNCTIONS USED TO ALLOCATE APPS/CGROUP MEMORIES (ARAL)
961 + *
962 + *****************************************************************/
963
964 /**
2987 - * Set Load mode
965 + * Allocate PID ARAL
966 + *
967 + * Allocate memory using ARAL functions to speed up processing.
968 + *
969 + * @param name the internal name used for allocated region.
970 + * @param size size of each element inside allocated space
971 *
2989 - * @param origin specify the configuration file loaded
972 + * @return It returns the address on success and NULL otherwise.
973 */
2991 -static inline void ebpf_set_load_mode(netdata_ebpf_load_mode_t load, netdata_ebpf_load_mode_t origin)
974 +ARAL *ebpf_allocate_pid_aral(char *name, size_t size)
975 {
2993 - int i;
2994 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
2995 - ebpf_modules[i].load &= ~NETDATA_EBPF_LOAD_METHODS;
2996 - ebpf_modules[i].load |= load | origin;
976 + static size_t max_elements = NETDATA_EBPF_ALLOC_MAX_PID;
977 + if (max_elements < NETDATA_EBPF_ALLOC_MIN_ELEMENTS) {
978 + netdata_log_error(
979 + "Number of elements given is too small, adjusting it for %d", NETDATA_EBPF_ALLOC_MIN_ELEMENTS);
980 + max_elements = NETDATA_EBPF_ALLOC_MIN_ELEMENTS;
981 }
982 +
983 + return aral_create(name, size, 0, 0, NULL, NULL, NULL, false, false, false);
984 }
985
3000 -/**
3001 - * Update mode
986 +/*****************************************************************
987 *
3003 - * @param str value read from configuration file.
3004 - * @param origin specify the configuration file loaded
3005 - */
3006 -static inline void epbf_update_load_mode(const char *str, netdata_ebpf_load_mode_t origin)
3007 -{
3008 - netdata_ebpf_load_mode_t load = epbf_convert_string_to_load_mode(str);
3009 -
3010 - ebpf_set_load_mode(load, origin);
3011 -}
988 + * FUNCTIONS USED TO CLEAN MEMORY AND OPERATE SYSTEM FILES
989 + *
990 + *****************************************************************/
991
992 /**
3014 - * Update Map per core
3015 - *
3016 - * Define the map type used with some hash tables.
993 + * Wait to avoid possible coredumps while process is closing.
994 */
3018 -static void ebpf_update_map_per_core()
995 +static inline void ebpf_check_before2go()
996 {
3020 - int value = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_MAPS_PER_CORE, CONFIG_BOOLEAN_YES);
3021 -
3022 - for (int i = 0; ebpf_modules[i].info.thread_name; i++) {
3023 - ebpf_modules[i].maps_per_core = value;
997 + usec_t max = 200 * USEC_PER_MS, step = 50 * USEC_PER_MS;
998 + int j;
999 + while (max) {
1000 + max -= step;
1001 + sleep_usec(step);
1002 + int active_count = 0;
1003 + netdata_mutex_lock(&ebpf_exit_cleanup);
1004 + for (j = 0; ebpf_modules[j].info.thread_name != NULL; j++) {
1005 + if (ebpf_modules[j].enabled < NETDATA_THREAD_EBPF_STOPPING)
1006 + active_count++;
1007 + }
1008 + netdata_mutex_unlock(&ebpf_exit_cleanup);
1009 + if (!active_count)
1010 + return;
1011 }
1012 +
1013 + netdata_log_error("eBPF cannot unload all threads on time, but it will go away");
1014 }
1015
1016 /**
3028 - * Read collector values
3029 - *
3030 - * @param disable_cgroups variable to store information related to cgroups.
3031 - * @param update_every value to overwrite the update frequency set by the server.
3032 - * @param origin specify the configuration file loaded
1017 + * Close the collector gracefully
1018 */
3034 -static void read_collector_values(int *disable_cgroups, int update_every, netdata_ebpf_load_mode_t origin)
1019 +static void ebpf_exit()
1020 {
3036 - // Read global section
3037 - const char *value;
3038 - if (inicfg_exists(&collector_config, EBPF_GLOBAL_SECTION, "load")) // Backward compatibility
3039 - value = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, "load", EBPF_CFG_LOAD_MODE_DEFAULT);
3040 - else
3041 - value = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_LOAD_MODE, EBPF_CFG_LOAD_MODE_DEFAULT);
3042 -
3043 - ebpf_how_to_load(value);
3044 -
3045 - btf_path = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_PROGRAM_PATH, EBPF_DEFAULT_BTF_PATH);
3046 -
1021 #ifdef LIBBPF_MAJOR_VERSION
3048 - default_btf = ebpf_load_btf_file(btf_path, EBPF_DEFAULT_BTF_FILE);
1022 + netdata_mutex_lock(&ebpf_exit_cleanup);
1023 + if (default_btf) {
1024 + btf__free(default_btf);
1025 + default_btf = NULL;
1026 + }
1027 + netdata_mutex_unlock(&ebpf_exit_cleanup);
1028 #endif
1029
3051 - value = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_TYPE_FORMAT, EBPF_CFG_DEFAULT_PROGRAM);
3052 -
3053 - epbf_update_load_mode(value, origin);
3054 -
3055 - ebpf_update_interval(update_every);
3056 -
3057 - ebpf_update_table_size();
1030 + char filename[FILENAME_MAX + 1];
1031 + ebpf_pid_file(filename, FILENAME_MAX);
1032 + if (unlink(filename) == -1 && errno != ENOENT)
1033 + netdata_log_error("Cannot remove PID file %s: %s", filename, strerror(errno));
1034
3059 - ebpf_update_lifetime();
1035 +#ifdef NETDATA_INTERNAL_CHECKS
1036 + netdata_log_error("Good bye world! I was PID %d", main_thread_id);
1037 +#endif
1038 + fprintf(stdout, "EXIT\n");
1039 + fflush(stdout);
1040
3061 - // This is kept to keep compatibility
3062 - uint32_t enabled = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, "disable apps", CONFIG_BOOLEAN_NO);
3063 - if (!enabled) {
3064 - // Apps is a positive sentence, so we need to invert the values to disable apps.
3065 - enabled = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_APPLICATION, CONFIG_BOOLEAN_YES);
3066 - enabled = (enabled == CONFIG_BOOLEAN_NO) ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
1041 + if (!ebpf_pre_exit_check_done) {
1042 + ebpf_check_before2go();
1043 + ebpf_pre_exit_check_done = true;
1044 }
3068 -
3069 - ebpf_set_apps_mode(!enabled);
3070 -
3071 - // Cgroup is a positive sentence, so we need to invert the values to disable apps.
3072 - // We are using the same pattern for cgroup and apps
3073 - enabled = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_CGROUP, CONFIG_BOOLEAN_NO);
3074 - *disable_cgroups = (enabled == CONFIG_BOOLEAN_NO) ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
3075 -
3076 - ebpf_update_map_per_core();
3077 -
3078 - // Read ebpf programs section
3079 - enabled = inicfg_get_boolean(
3080 - &collector_config,
3081 - EBPF_PROGRAMS_SECTION,
3082 - ebpf_modules[EBPF_MODULE_PROCESS_IDX].info.config_name,
3083 - CONFIG_BOOLEAN_YES);
3084 - if (enabled) {
3085 - ebpf_enable_chart(EBPF_MODULE_PROCESS_IDX, *disable_cgroups);
1045 + netdata_mutex_lock(&mutex_cgroup_shm);
1046 + if (shm_ebpf_cgroup.header) {
1047 + ebpf_unmap_cgroup_shared_memory();
1048 + shm_unlink(NETDATA_SHARED_MEMORY_EBPF_CGROUP_NAME);
1049 }
1050 + netdata_mutex_unlock(&mutex_cgroup_shm);
1051 + netdata_integration_cleanup_shm();
1052
3088 - // This is kept to keep compatibility
3089 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "network viewer", CONFIG_BOOLEAN_NO);
3090 - if (!enabled)
3091 - enabled = inicfg_get_boolean(
3092 - &collector_config,
3093 - EBPF_PROGRAMS_SECTION,
3094 - ebpf_modules[EBPF_MODULE_SOCKET_IDX].info.config_name,
3095 - CONFIG_BOOLEAN_NO);
3096 - if (enabled) {
3097 - ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_cgroups);
3098 - }
1053 + exit(0);
1054 +}
1055
3100 - // This is kept to keep compatibility
3101 - enabled = inicfg_get_boolean(
3102 - &collector_config, EBPF_PROGRAMS_SECTION, "network connection monitoring", CONFIG_BOOLEAN_YES);
3103 - if (!enabled)
3104 - enabled =
3105 - inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "network connections", CONFIG_BOOLEAN_YES);
3106 -
3107 - network_viewer_opt.enabled = enabled;
3108 - if (enabled) {
3109 - if (!ebpf_modules[EBPF_MODULE_SOCKET_IDX].enabled)
3110 - ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_cgroups);
3111 -
3112 - // Read network viewer section if network viewer is enabled
3113 - // This is kept here to keep backward compatibility
3114 - parse_network_viewer_section(&collector_config);
3115 - ebpf_parse_service_name_section(&collector_config);
3116 - }
1056 +/**
1057 + * Unload legacy code
1058 + *
1059 + * @param objects objects loaded from eBPF programs
1060 + * @param probe_links links from loader
1061 + */
1062 +void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links)
1063 +{
1064 + if (!probe_links || !objects)
1065 + return;
1066
3118 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "cachestat", CONFIG_BOOLEAN_NO);
3119 - if (enabled) {
3120 - ebpf_enable_chart(EBPF_MODULE_CACHESTAT_IDX, *disable_cgroups);
1067 + if ((uintptr_t)objects < 4096 || (uintptr_t)objects == (uintptr_t)-1) {
1068 + freez(probe_links);
1069 + return;
1070 }
1071
3123 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "sync", CONFIG_BOOLEAN_YES);
3124 - if (enabled) {
3125 - ebpf_enable_chart(EBPF_MODULE_SYNC_IDX, *disable_cgroups);
1072 + struct bpf_program *prog;
1073 + size_t j = 0;
1074 + bpf_object__for_each_program(prog, objects)
1075 + {
1076 + bpf_link__destroy(probe_links[j]);
1077 + j++;
1078 }
1079 + freez(probe_links);
1080
3128 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "dcstat", CONFIG_BOOLEAN_NO);
3129 - if (enabled) {
3130 - ebpf_enable_chart(EBPF_MODULE_DCSTAT_IDX, *disable_cgroups);
3131 - }
1081 + bpf_object__close(objects);
1082 +}
1083
3133 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "swap", CONFIG_BOOLEAN_NO);
3134 - if (enabled) {
3135 - ebpf_enable_chart(EBPF_MODULE_SWAP_IDX, *disable_cgroups);
1084 +void ebpf_unload_legacy_bpf(ebpf_module_t *em)
1085 +{
1086 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
1087 + ebpf_unload_legacy_code(em->objects, em->probe_links);
1088 + em->objects = NULL;
1089 + em->probe_links = NULL;
1090 }
1091 +}
1092
3138 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "vfs", CONFIG_BOOLEAN_NO);
3139 - if (enabled) {
3140 - ebpf_enable_chart(EBPF_MODULE_VFS_IDX, *disable_cgroups);
3141 - }
1093 +static void ebpf_socket_unload_bpf(ebpf_module_t *em)
1094 +{
1095 + ebpf_unload_legacy_bpf(em);
1096
3143 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "filesystem", CONFIG_BOOLEAN_NO);
3144 - if (enabled) {
3145 - ebpf_enable_chart(EBPF_MODULE_FILESYSTEM_IDX, *disable_cgroups);
1097 +#ifdef LIBBPF_MAJOR_VERSION
1098 + if (socket_bpf_obj) {
1099 + socket_bpf__destroy(socket_bpf_obj);
1100 + socket_bpf_obj = NULL;
1101 }
1102 +#endif
1103 +}
1104
3148 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "disk", CONFIG_BOOLEAN_NO);
3149 - if (enabled) {
3150 - ebpf_enable_chart(EBPF_MODULE_DISK_IDX, *disable_cgroups);
3151 - }
1105 +/**
1106 + * Read Local Ports
1107 + *
1108 + * Parse /proc/net/{tcp,udp} and get the ports Linux is listening.
1109 + *
1110 + * @param filename the proc file to parse.
1111 + * @param proto is the magic number associated to the protocol file we are reading.
1112 + */
1113 +void ebpf_stop_threads(int sig)
1114 +{
1115 + static int only_one = 0;
1116 + usec_t stop_started_ut = now_monotonic_usec();
1117 + pid_t current_tid = gettid_cached();
1118
3153 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "mount", CONFIG_BOOLEAN_YES);
3154 - if (enabled) {
3155 - ebpf_enable_chart(EBPF_MODULE_MOUNT_IDX, *disable_cgroups);
1119 + // Child thread should be closed by itself.
1120 + netdata_mutex_lock(&ebpf_exit_cleanup);
1121 + if (main_thread_id != gettid_cached() || only_one) {
1122 + netdata_mutex_unlock(&ebpf_exit_cleanup);
1123 + return;
1124 }
1125 + only_one = 1;
1126
3158 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "fd", CONFIG_BOOLEAN_YES);
3159 - if (enabled) {
3160 - ebpf_enable_chart(EBPF_MODULE_FD_IDX, *disable_cgroups);
3161 - }
1127 + netdata_log_info(
1128 + "EBPF SHUTDOWN: stop requested (signal=%d, main_tid=%d, current_tid=%d).", sig, main_thread_id, current_tid);
1129 + __atomic_store_n(&ebpf_plugin_exit, true, __ATOMIC_RELEASE);
1130
3163 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "hardirq", CONFIG_BOOLEAN_YES);
3164 - if (enabled) {
3165 - ebpf_enable_chart(EBPF_MODULE_HARDIRQ_IDX, *disable_cgroups);
1131 + int i;
1132 + for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
1133 + if (ebpf_modules[i].enabled < NETDATA_THREAD_EBPF_STOPPING && ebpf_modules[i].thread &&
1134 + ebpf_modules[i].thread->thread) {
1135 + nd_thread_signal_cancel(ebpf_modules[i].thread->thread);
1136 +#ifdef NETDATA_DEV_MODE
1137 + netdata_log_info("Sending cancel for thread %s", ebpf_modules[i].info.thread_name);
1138 +#endif
1139 + }
1140 }
1141 + netdata_mutex_unlock(&ebpf_exit_cleanup);
1142
3168 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "softirq", CONFIG_BOOLEAN_YES);
3169 - if (enabled) {
3170 - ebpf_enable_chart(EBPF_MODULE_SOFTIRQ_IDX, *disable_cgroups);
1143 + for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
1144 + if (ebpf_modules[i].enabled < NETDATA_THREAD_EBPF_STOPPED && ebpf_threads[i].thread) {
1145 + netdata_log_info(
1146 + "EBPF SHUTDOWN: about to join module[%d]='%s' (state=%u).",
1147 + i,
1148 + ebpf_modules[i].info.thread_name,
1149 + ebpf_modules[i].enabled);
1150 + usec_t join_started_ut = now_monotonic_usec();
1151 + nd_thread_join(ebpf_threads[i].thread);
1152 + usec_t join_duration_ut = now_monotonic_usec() - join_started_ut;
1153 + netdata_log_info(
1154 + "EBPF SHUTDOWN: joined '%s' in %llums.",
1155 + ebpf_modules[i].info.thread_name,
1156 + (unsigned long long)(join_duration_ut / USEC_PER_MS));
1157 + }
1158 }
1159
3173 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "oomkill", CONFIG_BOOLEAN_YES);
3174 - if (enabled) {
3175 - ebpf_enable_chart(EBPF_MODULE_OOMKILL_IDX, *disable_cgroups);
3176 - }
1160 + netdata_mutex_lock(&mutex_cgroup_shm);
1161 + nd_thread_signal_cancel(cgroup_integration_thread.thread);
1162 +#ifdef NETDATA_DEV_MODE
1163 + netdata_log_info("Sending cancel for thread %s", cgroup_integration_thread.name);
1164 +#endif
1165 + netdata_mutex_unlock(&mutex_cgroup_shm);
1166
3178 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "shm", CONFIG_BOOLEAN_YES);
3179 - if (enabled) {
3180 - ebpf_enable_chart(EBPF_MODULE_SHM_IDX, *disable_cgroups);
1167 + usec_t before_checks_ut = now_monotonic_usec();
1168 + if (!ebpf_pre_exit_check_done) {
1169 + ebpf_check_before2go();
1170 + ebpf_pre_exit_check_done = true;
1171 }
1172 + usec_t checks_duration_ut = now_monotonic_usec() - before_checks_ut;
1173 + netdata_log_info(
1174 + "EBPF SHUTDOWN: post-cancel checks finished in %llums.",
1175 + (unsigned long long)(checks_duration_ut / USEC_PER_MS));
1176
3183 - enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "mdflush", CONFIG_BOOLEAN_NO);
3184 - if (enabled) {
3185 - ebpf_enable_chart(EBPF_MODULE_MDFLUSH_IDX, *disable_cgroups);
3186 - }
3187 -}
1177 + // BPF unload is handled by each module's exit function (in parallel with thread shutdown).
1178 + // During forced shutdown, the kernel cleans up BPF programs automatically on process exit.
1179 + // Sequential unload here would add several seconds of delay with no benefit.
1180
3189 -static void ebpf_set_ipc_value(const char *integration)
3190 -{
3191 - if (!strcmp(integration, NETDATA_EBPF_IPC_INTEGRATION_SHM)) {
3192 - integration_with_collectors = NETDATA_EBPF_INTEGRATION_SHM;
3193 - return;
3194 - } else if (!strcmp(integration, NETDATA_EBPF_IPC_INTEGRATION_SOCKET)) {
3195 - integration_with_collectors = NETDATA_EBPF_INTEGRATION_SOCKET;
3196 - return;
3197 - }
3198 - integration_with_collectors = NETDATA_EBPF_INTEGRATION_DISABLED;
3199 -}
1181 + usec_t total_duration_ut = now_monotonic_usec() - stop_started_ut;
1182 + netdata_log_info(
1183 + "EBPF SHUTDOWN: total stop duration %llums.", (unsigned long long)(total_duration_ut / USEC_PER_MS));
1184
3201 -static void ebpf_parse_ipc_section()
3202 -{
3203 - const char *integration = inicfg_get(
3204 - &collector_config,
3205 - NETDATA_EBPF_IPC_SECTION,
3206 - NETDATA_EBPF_IPC_INTEGRATION,
3207 - NETDATA_EBPF_IPC_INTEGRATION_DISABLED);
3208 - ebpf_set_ipc_value(integration);
3209 -
3210 - ipc_sockets.default_bind_to = inicfg_get(
3211 - &collector_config, NETDATA_EBPF_IPC_SECTION, NETDATA_EBPF_IPC_BIND_TO, NETDATA_EBPF_IPC_BIND_TO_DEFAULT);
3212 -
3213 - ipc_sockets.backlog =
3214 - (int)inicfg_get_number(&collector_config, NETDATA_EBPF_IPC_SECTION, NETDATA_EBPF_IPC_BACKLOG, 20);
1185 + ebpf_exit();
1186 }
1187
1188 /**
3218 - * Load collector config
3219 - *
3220 - * @param path the path where the file ebpf.conf is stored.
3221 - * @param disable_cgroups variable to store the information about cgroups plugin status.
3222 - * @param update_every value to overwrite the update frequency set by the server.
1189 + * Start Pthread Variable
1190 *
3224 - * @return 0 on success and -1 otherwise.
1191 + * This function starts all
1192 */
3226 -static int ebpf_load_collector_config(char *path, int *disable_cgroups, int update_every)
1193 +static void ebpf_mutex_initialize()
1194 {
3228 - char lpath[4096];
3229 - netdata_ebpf_load_mode_t origin;
3230 -
3231 - snprintf(lpath, 4095, "%s/%s", path, NETDATA_EBPF_CONFIG_FILE);
3232 - if (!inicfg_load(&collector_config, lpath, 0, NULL)) {
3233 - snprintf(lpath, 4095, "%s/%s", path, NETDATA_EBPF_OLD_CONFIG_FILE);
3234 - if (!inicfg_load(&collector_config, lpath, 0, NULL)) {
3235 - return -1;
3236 - }
3237 - origin = EBPF_LOADED_FROM_STOCK;
3238 - } else
3239 - origin = EBPF_LOADED_FROM_USER;
3240 -
3241 - read_collector_values(disable_cgroups, update_every, origin);
3242 - ebpf_parse_ipc_section();
1195 + netdata_mutex_init(&lock);
1196 + netdata_mutex_init(&ebpf_exit_cleanup);
1197 + netdata_mutex_init(&collect_data_mutex);
1198 + netdata_mutex_init(&mutex_cgroup_shm);
1199 + rw_spinlock_init(&ebpf_judy_pid.index.rw_spinlock);
1200 +}
1201
3244 - return 0;
1202 +/**
1203 + * Allocate the vectors used for all threads.
1204 + */
1205 +static void ebpf_allocate_common_vectors()
1206 +{
1207 + ebpf_judy_pid.pid_table =
1208 + ebpf_allocate_pid_aral(NETDATA_EBPF_PID_SOCKET_ARAL_TABLE_NAME, sizeof(netdata_ebpf_judy_pid_stats_t));
1209 + ebpf_aral_init();
1210 }
1211
1212 /**
@@ -3275,18 +1240,7 @@ static void ebpf_set_global_variables()
1240 isrh = get_redhat_release();
1241 pid_max = os_get_system_pid_max();
1242 running_on_kernel = ebpf_get_kernel_version();
3278 - memset(pids_fd, -1, sizeof(pids_fd));
3279 -}
3280 -
3281 -/**
3282 - * Load collector config
3283 - */
3284 -static inline void ebpf_load_thread_config()
3285 -{
3286 - int i;
3287 - for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
3288 - ebpf_update_module(&ebpf_modules[i], default_btf, running_on_kernel, isrh);
3289 - }
1243 + ebpf_reset_pid_map_fds();
1244 }
1245
1246 /**
@@ -3342,7 +1296,6 @@ static void ebpf_parse_args(int argc, char **argv)
1296 if (!freq)
1297 freq = EBPF_DEFAULT_UPDATE_EVERY;
1298
3345 - //rw_spinlock_write_lock(&network_viewer_opt.rw_spinlock);
1299 if (ebpf_load_collector_config(ebpf_user_config_dir, &disable_cgroups, freq)) {
1300 netdata_log_info(
1301 "Does not have a configuration file inside `%s/ebpf.d.conf. It will try to load stock file.",
@@ -3353,7 +1306,6 @@ static void ebpf_parse_args(int argc, char **argv)
1306 }
1307
1308 ebpf_load_thread_config();
3356 - //rw_spinlock_write_unlock(&network_viewer_opt.rw_spinlock);
1309
1310 while (1) {
1311 int c = getopt_long_only(argc, argv, "", long_options, &option_index);
@@ -3603,8 +1555,8 @@ static void ebpf_parse_args(int argc, char **argv)
1555 *****************************************************************/
1556
1557 static char *load_event_stat[NETDATA_EBPF_LOAD_STAT_END] = {"legacy", "co-re"};
3606 -static char *memlock_stat = {"memory_locked"};
3607 -static char *hash_table_stat = {"hash_table"};
1558 +static char *memlock_stat = "memory_locked";
1559 +static char *hash_table_stat = "hash_table";
1560 static char *hash_table_core[NETDATA_EBPF_LOAD_STAT_END] = {"per_core", "unique"};
1561
1562 /**
@@ -3701,8 +1653,8 @@ void ebpf_send_statistic_data()
1653 ebpf_user_mem_stat_t ipc_data;
1654 netdata_integration_current_ipc_data(&ipc_data);
1655 NETDATA_DOUBLE ipc_value = 0.0;
3704 - if (ipc_data.total > 0 )
3705 - ipc_value = ( (NETDATA_DOUBLE)ipc_data.current/(NETDATA_DOUBLE)ipc_data.total )*100.0;
1656 + if (ipc_data.total > 0)
1657 + ipc_value = ((NETDATA_DOUBLE)ipc_data.current / (NETDATA_DOUBLE)ipc_data.total) * 100.0;
1658 ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, NETDATA_EBPF_IPC_USAGE, "");
1659 write_chart_dimension("positions", (long long)ipc_value);
1660 ebpf_write_end_chart();
@@ -3725,14 +1677,15 @@ void ebpf_send_statistic_data()
1677
1678 for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
1679 ebpf_module_t *wem = &ebpf_modules[i];
3728 - if (!wem->functions.fnct_routine)
1680 + if (!wem->functions.fnct_routine || !wem->functions.fcnt_thread_chart_name ||
1681 + !wem->functions.fcnt_thread_lifetime_name)
1682 continue;
1683
3731 - ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, (char *)wem->functions.fcnt_thread_chart_name, "");
1684 + ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, wem->functions.fcnt_thread_chart_name, "");
1685 write_chart_dimension((char *)wem->info.thread_name, (wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ? 1 : 0);
1686 ebpf_write_end_chart();
1687
3735 - ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, (char *)wem->functions.fcnt_thread_lifetime_name, "");
1688 + ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, wem->functions.fcnt_thread_lifetime_name, "");
1689 write_chart_dimension(
1690 (char *)wem->info.thread_name,
1691 (wem->lifetime && wem->enabled < NETDATA_THREAD_EBPF_STOPPING) ?
@@ -3855,10 +1808,7 @@ static inline void ebpf_create_statistic_ipc_usage(int update_every)
1808 update_every,
1809 NETDATA_EBPF_MODULE_NAME_PROCESS);
1810
3858 - ebpf_write_global_dimension(
3859 - "positions",
3860 - "positions",
3861 - ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
1811 + ebpf_write_global_dimension("positions", "positions", ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
1812 }
1813
1814 /**
@@ -4156,7 +2106,7 @@ static char *ebpf_get_process_name(pid_t pid)
2106 {
2107 char *name = NULL;
2108 char filename[FILENAME_MAX + 1];
4159 - snprintfz(filename, FILENAME_MAX, "/proc/%d/status", pid);
2109 + snprintfz(filename, sizeof(filename) - 1, "/proc/%d/status", pid);
2110
2111 procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
2112 if (unlikely(!ff)) {
@@ -4165,8 +2115,10 @@ static char *ebpf_get_process_name(pid_t pid)
2115 }
2116
2117 ff = procfile_readall(ff);
4168 - if (unlikely(!ff))
2118 + if (unlikely(!ff)) {
2119 + procfile_close(ff);
2120 return name;
2121 + }
2122
2123 unsigned long i, lines = procfile_lines(ff);
2124 for (i = 0; i < lines; i++) {
@@ -4225,9 +2177,7 @@ void ebpf_validate_data_sharing_selection()
2177 }
2178 }
2179
4228 - // TODO: MODIFY IN NEXT PRs THE OPTION TO ALSO USE SOCKET
2180 if (enabled && integration_with_collectors != NETDATA_EBPF_INTEGRATION_SHM) {
4230 - //if (enabled && integration_with_collectors == NETDATA_EBPF_INTEGRATION_DISABLED) {
2181 integration_with_collectors = NETDATA_EBPF_INTEGRATION_SHM;
2182 }
2183 }
@@ -4254,6 +2204,7 @@ static void ebpf_initialize_data_sharing()
2204 ebpf_set_apps_mode(NETDATA_EBPF_APPS_FLAG_NO);
2205 ebpf_disable_cgroups();
2206 }
2207 + break;
2208 case NETDATA_EBPF_INTEGRATION_DISABLED:
2209 default:
2210 break;
@@ -4274,14 +2225,12 @@ static void ebpf_kill_previous_process(char *filename, pid_t pid)
2225 if (!old_pid)
2226 return;
2227
4277 - // Process is not running
2228 char *prev_name = ebpf_get_process_name(old_pid);
2229 if (!prev_name)
2230 return;
2231
2232 char *current_name = ebpf_get_process_name(pid);
4283 -
4284 - if (!strcmp(prev_name, current_name))
2233 + if (current_name && !strcmp(prev_name, current_name))
2234 kill(old_pid, SIGKILL);
2235
2236 freez(prev_name);
@@ -4301,7 +2250,7 @@ static void ebpf_kill_previous_process(char *filename, pid_t pid)
2250 */
2251 void ebpf_pid_file(char *filename, size_t length)
2252 {
4304 - snprintfz(filename, length, "%s/var/run/ebpf.pid", netdata_configured_host_prefix);
2253 + snprintfz(filename, length - 1, "%s/var/run/ebpf.pid", netdata_configured_host_prefix);
2254 }
2255
2256 /**
@@ -4328,11 +2277,18 @@ static void ebpf_manage_pid(pid_t pid)
2277 static void ebpf_set_static_routine()
2278 {
2279 int i;
4331 - for (i = 0; ebpf_modules[i].info.thread_name; i++) {
2280 + for (i = 0; ebpf_modules[i].info.thread_name != NULL; i++) {
2281 ebpf_threads[i].start_routine = ebpf_modules[i].functions.start_routine;
2282 }
2283 }
2284
2285 +static void ebpf_signal_stop_handler(int sig)
2286 +{
2287 + // Async-signal-safe stop request: actual shutdown is handled by main thread flow.
2288 + if (!ebpf_stop_signal)
2289 + ebpf_stop_signal = (sig > 0) ? sig : 1;
2290 +}
2291 +
2292 /**
2293 * Entry point
2294 *
@@ -4346,6 +2302,8 @@ int main(int argc, char **argv)
2302 nd_log_initialize_for_external_plugins(NETDATA_EBPF_PLUGIN_NAME);
2303 netdata_threads_init_for_external_plugins(0);
2304
2305 + libjudy_malloc_init();
2306 +
2307 ebpf_set_global_variables();
2308 if (ebpf_can_plugin_load_code(running_on_kernel, NETDATA_EBPF_PLUGIN_NAME))
2309 return 2;
@@ -4358,10 +2316,10 @@ int main(int argc, char **argv)
2316 ebpf_parse_args(argc, argv);
2317 ebpf_manage_pid(getpid());
2318
4361 - signal(SIGINT, ebpf_stop_threads);
4362 - signal(SIGQUIT, ebpf_stop_threads);
4363 - signal(SIGTERM, ebpf_stop_threads);
4364 - signal(SIGPIPE, ebpf_stop_threads);
2319 + signal(SIGINT, ebpf_signal_stop_handler);
2320 + signal(SIGQUIT, ebpf_signal_stop_handler);
2321 + signal(SIGTERM, ebpf_signal_stop_handler);
2322 + signal(SIGPIPE, ebpf_signal_stop_handler);
2323
2324 ebpf_mutex_initialize();
2325
@@ -4410,6 +2368,7 @@ int main(int argc, char **argv)
2368 }
2369 st->thread = nd_thread_create(st->name, NETDATA_THREAD_OPTION_DEFAULT, st->start_routine, em);
2370 } else {
2371 + st->enabled = 0;
2372 em->lifetime = EBPF_DEFAULT_LIFETIME;
2373 }
2374 }
@@ -4422,6 +2381,14 @@ int main(int argc, char **argv)
2381 for (; !ebpf_plugin_stop(); global_iterations_counter++) {
2382 (void)heartbeat_next(&hb);
2383
2384 + // Skip all work (including expensive apps-parsing) if shutdown was requested
2385 + // while we were sleeping. Without this check the main thread runs
2386 + // ebpf_parse_proc_files()+ebpf_create_apps_charts() while holding lock +
2387 + // collect_data_mutex, blocking module threads from exiting and doubling
2388 + // shutdown time when fd/process/socket/vfs modules are enabled.
2389 + if (ebpf_plugin_stop())
2390 + break;
2391 +
2392 if (global_iterations_counter % EBPF_DEFAULT_UPDATE_EVERY == 0) {
2393 netdata_mutex_lock(&lock);
2394 ebpf_create_statistic_charts(EBPF_DEFAULT_UPDATE_EVERY);
@@ -4444,7 +2411,7 @@ int main(int argc, char **argv)
2411 }
2412 }
2413
4447 - ebpf_stop_threads(0);
2414 + ebpf_stop_threads((int)ebpf_stop_signal);
2415
2416 return 0;
2417 }
src/collectors/ebpf.plugin/ebpf.d.conf
+2 -2
@@ -25,7 +25,7 @@
25 ebpf load mode = entry
26 apps = no
27 cgroups = no
28 - update every = 5
28 + update every = 10
29 pid table size = 32768
30 btf path = /sys/kernel/btf/
31 maps per core = yes
@@ -70,7 +70,7 @@
70 process = no
71 shm = no
72 socket = no
73 - softirq = yes
73 + softirq = no
74 sync = no
75 swap = no
76 vfs = no
src/collectors/ebpf.plugin/ebpf.d/functions.conf
+1 -1
@@ -1,3 +1,3 @@
1 #[global]
2 -# update every = 5
2 +# update every = 10
3
src/collectors/ebpf.plugin/ebpf.h
+64 -58
@@ -12,6 +12,7 @@
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 +#include <time.h>
16 #include <unistd.h>
17 #include <dlfcn.h>
18
@@ -56,7 +57,7 @@ extern struct mount_bpf *mount_bpf_obj;
57 extern struct mdflush_bpf *mdflush_bpf_obj;
58 extern struct shm_bpf *shm_bpf_obj;
59 extern struct socket_bpf *socket_bpf_obj;
59 -extern struct swap_bpf *bpf_obj;
60 +extern struct swap_bpf *swap_bpf_obj;
61 extern struct vfs_bpf *vfs_bpf_obj;
62 extern struct process_bpf *process_bpf_obj;
63 #endif
@@ -204,46 +205,6 @@ void ebpf_global_labels(
205 int *algorithm,
206 int end);
207
207 -void ebpf_write_chart_cmd(
208 - char *type,
209 - char *id,
210 - char *suffix,
211 - char *title,
212 - char *units,
213 - char *family,
214 - char *charttype,
215 - char *context,
216 - int order,
217 - int update_every,
218 - char *module);
219 -
220 -void ebpf_write_global_dimension(char *name, char *id, char *algorithm);
221 -
222 -void ebpf_create_global_dimension(void *ptr, int end);
223 -
224 -void ebpf_create_chart(
225 - char *type,
226 - char *id,
227 - char *title,
228 - char *units,
229 - char *family,
230 - char *context,
231 - char *charttype,
232 - int order,
233 - void (*ncd)(void *, int),
234 - void *move,
235 - int end,
236 - int update_every,
237 - char *module);
238 -
239 -void write_chart_dimension(char *dim, long long value);
240 -
241 -void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end);
242 -
243 -void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end);
244 -
245 -void write_io_chart(char *chart, char *family, char *dwrite, long long vwrite, char *dread, long long vread);
246 -
208 /**
209 * Create Chart labels
210 *
@@ -273,7 +234,7 @@ static inline void ebpf_commit_label()
234 * @param name the chart name
235 * @param metric the chart suffix (used with apps and cgroups)
236 */
276 -static inline void ebpf_write_begin_chart(char *family, char *name, char *metric)
237 +static inline void ebpf_write_begin_chart(const char *family, const char *name, const char *metric)
238 {
239 printf("BEGIN %s.%s%s\n", family, name, metric);
240 }
@@ -320,30 +281,33 @@ extern struct btf *default_btf;
281 extern void *default_btf;
282 #endif
283
284 +extern uint32_t integration_with_collectors;
285 +extern int running_on_kernel;
286 +extern int isrh;
287 +extern const char *btf_path;
288 +
289 // Socket functions and variables
290 // Common functions
291 void ebpf_process_create_apps_charts(struct ebpf_module *em, void *ptr);
292 void ebpf_socket_create_apps_charts(struct ebpf_module *em, void *ptr);
293 void ebpf_cachestat_create_apps_charts(struct ebpf_module *em, void *root);
328 -void ebpf_one_dimension_write_charts(char *family, char *chart, char *dim, long long v1);
294 +
295 +// BPF teardown callbacks — called by main thread after all module threads have been joined
296 +void ebpf_unload_legacy_bpf(ebpf_module_t *em); // legacy-only modules: process, disk, softirq, oomkill, mdflush
297 +void ebpf_cachestat_unload_bpf(ebpf_module_t *em);
298 +void ebpf_dcstat_unload_bpf(ebpf_module_t *em);
299 +void ebpf_swap_unload_bpf(ebpf_module_t *em);
300 +void ebpf_vfs_unload_bpf(ebpf_module_t *em);
301 +void ebpf_filesystem_unload_bpf(ebpf_module_t *em);
302 +void ebpf_mount_unload_bpf(ebpf_module_t *em);
303 +void ebpf_fd_unload_bpf(ebpf_module_t *em);
304 +void ebpf_shm_unload_bpf(ebpf_module_t *em);
305 +void ebpf_sync_unload_bpf(ebpf_module_t *em);
306 collected_number get_value_from_structure(char *basis, size_t offset);
307 void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em);
331 -void ebpf_write_chart_obsolete(
332 - char *type,
333 - char *id,
334 - char *suffix,
335 - char *title,
336 - char *units,
337 - char *family,
338 - char *charttype,
339 - char *context,
340 - int order,
341 - int update_every);
342 -void write_histogram_chart(char *family, char *name, const netdata_idx_t *hist, char **dimensions, uint32_t end);
308 void ebpf_update_disabled_plugin_stats(ebpf_module_t *em);
309 ARAL *ebpf_allocate_pid_aral(char *name, size_t size);
310 void ebpf_unload_legacy_code(struct bpf_object *objects, struct bpf_link **probe_links);
346 -
311 void ebpf_read_global_table_stats(
312 netdata_idx_t *stats,
313 netdata_idx_t *values,
@@ -354,19 +318,61 @@ void ebpf_read_global_table_stats(
318 void **ebpf_judy_insert_unsafe(PPvoid_t arr, Word_t key);
319 netdata_ebpf_judy_pid_stats_t *ebpf_get_pid_from_judy_unsafe(PPvoid_t judy_array, uint32_t pid);
320
357 -void parse_network_viewer_section(struct config *cfg);
321 void ebpf_clean_ip_structure(ebpf_network_viewer_ip_list_t **clean);
322 void ebpf_clean_port_structure(ebpf_network_viewer_port_list_t **clean);
323 void ebpf_read_local_addresses_unsafe();
324
325 extern ebpf_filesystem_partitions_t localfs[];
326 extern ebpf_sync_syscalls_t local_syscalls[];
327 +extern volatile sig_atomic_t ebpf_stop_signal;
328 extern bool ebpf_plugin_exit;
329 extern uint64_t collect_pids;
330
331 static inline bool ebpf_plugin_stop(void)
332 {
369 - return __atomic_load_n(&ebpf_plugin_exit, __ATOMIC_ACQUIRE) || nd_thread_signaled_to_cancel();
333 + return __atomic_load_n(&ebpf_plugin_exit, __ATOMIC_ACQUIRE) ||
334 + ebpf_stop_signal ||
335 + nd_thread_signaled_to_cancel();
336 +}
337 +
338 +static inline bool ebpf_module_thread_has_valid_state(ebpf_module_t *em)
339 +{
340 + if (likely(em->enabled == NETDATA_THREAD_EBPF_RUNNING || em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING))
341 + return true;
342 +
343 + collector_error("Cannot start thread %s with invalid state %u.", em->info.thread_name, (unsigned int)em->enabled);
344 + return false;
345 +}
346 +
347 +static inline bool ebpf_shm_sem_wait_or_stop(sem_t *sem)
348 +{
349 + if (unlikely(!sem || sem == SEM_FAILED)) {
350 + errno = EINVAL;
351 + return false;
352 + }
353 +
354 + while (!ebpf_plugin_stop()) {
355 + struct timespec ts;
356 + if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
357 + return false;
358 +
359 + ts.tv_nsec += 200 * 1000 * 1000;
360 + if (ts.tv_nsec >= 1000000000L) {
361 + ts.tv_sec += ts.tv_nsec / 1000000000L;
362 + ts.tv_nsec %= 1000000000L;
363 + }
364 +
365 + if (sem_timedwait(sem, &ts) == 0)
366 + return true;
367 +
368 + if (errno == ETIMEDOUT || errno == EINTR)
369 + continue;
370 +
371 + return false;
372 + }
373 +
374 + errno = ECANCELED;
375 + return false;
376 }
377
378 void ebpf_stop_threads(int sig);
src/collectors/ebpf.plugin/ebpf_apps.c
+86 -102
@@ -79,6 +79,9 @@ size_t zero_all_targets(struct ebpf_target *root)
79 size_t count = 0;
80
81 for (w = root; w; w = w->next) {
82 + if (ebpf_plugin_stop())
83 + break;
84 +
85 count++;
86
87 if (unlikely(w->root_pid)) {
@@ -106,6 +109,9 @@ void clean_apps_groups_target(struct ebpf_target *agrt)
109 {
110 struct ebpf_target *current_target;
111 while (agrt) {
112 + if (ebpf_plugin_stop())
113 + break;
114 +
115 current_target = agrt;
116 agrt = current_target->target;
117
@@ -144,6 +150,9 @@ get_apps_groups_target(struct ebpf_target **agrt, const char *id, struct ebpf_ta
150 // find if it already exists
151 struct ebpf_target *w, *last = *agrt;
152 for (w = *agrt; w; w = w->next) {
153 + if (ebpf_plugin_stop())
154 + break;
155 +
156 if (w->idhash == hash && strncmp(nid, w->id, EBPF_MAX_NAME) == 0)
157 return w;
158
@@ -354,7 +363,6 @@ ebpf_pid_data_t *ebpf_find_or_create_pid_data(pid_t pid)
363 return pid_data;
364 }
365
357 -//ebpf_pid_data_t *ebpf_pids = NULL; // to avoid allocations, we pre-allocate the entire pid space.
366 ebpf_pid_data_t *ebpf_pids_link_list = NULL; // global list of all processes running
367
368 size_t ebpf_all_pids_count = 0; // the number of processes running read from /proc
@@ -367,19 +375,33 @@ struct ebpf_target *apps_groups_default_target = NULL, // the default target
375
376 size_t apps_groups_targets_count = 0; // # of apps_groups.conf targets
377
370 -int pids_fd[NETDATA_EBPF_PIDS_END_IDX];
378 +static int ebpf_pid_map_fds[NETDATA_EBPF_PIDS_END_IDX];
379 +
380 +void ebpf_reset_pid_map_fds(void)
381 +{
382 + memset(ebpf_pid_map_fds, -1, sizeof(ebpf_pid_map_fds));
383 +}
384 +
385 +void ebpf_set_pid_map_fd(int idx, int fd)
386 +{
387 + if (unlikely(idx < 0 || idx >= NETDATA_EBPF_PIDS_END_IDX))
388 + return;
389 +
390 + ebpf_pid_map_fds[idx] = fd;
391 +}
392 +
393 +int ebpf_get_pid_map_fd(int idx)
394 +{
395 + if (unlikely(idx < 0 || idx >= NETDATA_EBPF_PIDS_END_IDX))
396 + return -1;
397 +
398 + return ebpf_pid_map_fds[idx];
399 +}
400
401 // ----------------------------------------------------------------------------
402 // internal counters
403
375 -static size_t
376 - // global_iterations_counter = 1,
377 - //calls_counter = 0,
378 - // file_counter = 0,
379 - // filenames_allocated_counter = 0,
380 - // inodes_changed_counter = 0,
381 - // links_changed_counter = 0,
382 - targets_assignment_counter = 0;
404 +static size_t targets_assignment_counter = 0;
405
406 // ----------------------------------------------------------------------------
407 // debugging
@@ -426,6 +448,9 @@ static inline void assign_target_to_pid(ebpf_pid_data_t *p)
448 struct ebpf_target *w;
449 bool assigned = false;
450 for (w = apps_groups_root_target; w; w = w->next) {
451 + if (ebpf_plugin_stop())
452 + break;
453 +
454 // if(debug_enabled || (p->target && p->target->debug_enabled)) debug_log_int("\t\tcomparing '%s' with '%s'", w->compare, p->comm);
455
456 // find it - 4 cases:
@@ -495,12 +520,11 @@ static inline int read_proc_pid_cmdline(ebpf_pid_data_t *p, char *cmdline)
520 cmdline[i] = ' ';
521 }
522
498 - debug_log("Read file '%s' contents: %s", filename, p->cmdline);
499 -
523 ret = 1;
524
525 cleanup:
503 - p->cmdline[0] = '\0';
526 + if (p->cmdline)
527 + p->cmdline[0] = '\0';
528
529 return ret;
530 }
@@ -545,8 +569,8 @@ static inline int read_proc_pid_stat(ebpf_pid_data_t *p)
569 p->ppid = ppid;
570
571 char cmdline[MAX_CMDLINE + 1];
548 - p->cmdline = cmdline;
549 - read_proc_pid_cmdline(p, cmdline);
572 + if (read_proc_pid_cmdline(p, cmdline))
573 + p->cmdline = cmdline; /* point at filled buffer so assign_target_to_pid can match *pattern* rules */
574 if (strcmp(p->comm, comm) != 0) {
575 if (unlikely(debug_enabled)) {
576 if (p->comm[0])
@@ -557,10 +581,11 @@ static inline int read_proc_pid_stat(ebpf_pid_data_t *p)
581
582 strncpyz(p->comm, comm, EBPF_MAX_COMPARE_NAME);
583 }
584 +
585 if (!p->target)
586 assign_target_to_pid(p);
587
563 - p->cmdline = NULL;
588 + p->cmdline = NULL; /* cmdline is stack-local; do not let it escape this frame */
589
590 if (unlikely(debug_enabled || (p->target && p->target->debug_enabled)))
591 debug_log_int(
@@ -618,14 +643,17 @@ static inline void link_all_processes_to_their_parents(void)
643 for (p = ebpf_pids_link_list; p; p = p->next) {
644 // for each process found
645
646 + // Reset before the stop check: breaking early must not leave stale parent
647 + // pointers that apply_apps_groups_targets_inheritance() would dereference.
648 p->parent = NULL;
649
650 + if (ebpf_plugin_stop())
651 + break;
652 +
653 if (unlikely(!p->ppid)) {
624 - p->parent = NULL;
654 continue;
655 }
656
628 - // pp = &ebpf_pids[p->ppid];
657 pp = ebpf_find_pid_data(p->ppid);
658 if (likely(pp && pp->pid)) {
659 p->parent = pp;
@@ -648,139 +676,84 @@ static inline void link_all_processes_to_their_parents(void)
676
677 /**
678 * Aggregate PIDs to targets.
679 + *
680 + * This function performs target inheritance iteratively to ensure
681 + * proper propagation even when children appear before parents in the list.
682 + * Algorithm:
683 + * 1. Propagate targets from parent to children without targets (iterative)
684 + * 2. Merge leaf processes upward to their parents (iterative)
685 + * 3. Assign default target to unmerged top-level processes
686 + * 4. Propagate targets to merged children via their parents (iterative)
687 */
688 static void apply_apps_groups_targets_inheritance(void)
689 {
690 + int sortlist = 1;
691 struct ebpf_pid_data *p = NULL;
692
656 - // children that do not have a target
657 - // inherit their target from their parent
658 - int found = 1, loops = 0;
693 + ebpf_pid_data_t *pid_entry = ebpf_find_or_create_pid_data(INIT_PID);
694 + pid_entry->target = apps_groups_default_target;
695 +
696 + pid_entry = ebpf_find_or_create_pid_data(0);
697 + pid_entry->target = apps_groups_default_target;
698 +
699 + int found = 1;
700 while (found) {
660 - if (unlikely(debug_enabled))
661 - loops++;
701 found = 0;
702 for (p = ebpf_pids_link_list; p; p = p->next) {
664 - // if this process does not have a target
665 - // and it has a parent
666 - // and its parent has a target
667 - // then, set the parent's target to this process
703 if (unlikely(!p->target && p->parent && p->parent->target)) {
704 p->target = p->parent->target;
705 found++;
671 -
672 - if (debug_enabled || (p->target && p->target->debug_enabled))
673 - debug_log_int(
674 - "TARGET INHERITANCE: %s is inherited by %u (%s) from its parent %d (%s).",
675 - p->target->name,
676 - p->pid,
677 - p->comm,
678 - p->parent->pid,
679 - p->parent->comm);
706 }
707 }
708 }
709
684 - // find all the procs with 0 childs and merge them to their parents
685 - // repeat, until nothing more can be done.
686 - int sortlist = 1;
710 + for (p = ebpf_pids_link_list; p; p = p->next) {
711 + if (unlikely(!p->sortlist && !p->children_count))
712 + p->sortlist = sortlist++;
713 + }
714 +
715 found = 1;
716 while (found) {
689 - if (unlikely(debug_enabled))
690 - loops++;
717 found = 0;
692 -
718 for (p = ebpf_pids_link_list; p; p = p->next) {
694 - if (unlikely(!p->sortlist && !p->children_count))
695 - p->sortlist = sortlist++;
719 + if (ebpf_plugin_stop())
720 + break;
721
722 if (unlikely(
698 - !p->children_count // if this process does not have any children
699 - && !p->merged // and is not already merged
700 - && p->parent // and has a parent
701 - && p->parent->children_count // and its parent has children
702 - // and the target of this process and its parent is the same,
703 - // or the parent does not have a target
704 - && (p->target == p->parent->target || !p->parent->target) &&
705 - p->ppid != INIT_PID // and its parent is not init
706 - )) {
707 - // mark it as merged
723 + !p->children_count && !p->merged && p->parent && p->parent->children_count &&
724 + (p->target == p->parent->target || !p->parent->target) && p->ppid != INIT_PID)) {
725 p->parent->children_count--;
726 p->merged = 1;
727
711 - // the parent inherits the child's target, if it does not have a target itself
712 - if (unlikely(p->target && !p->parent->target)) {
728 + if (unlikely(p->target && !p->parent->target))
729 p->parent->target = p->target;
730
715 - if (debug_enabled || (p->target && p->target->debug_enabled))
716 - debug_log_int(
717 - "TARGET INHERITANCE: %s is inherited by %d (%s) from its child %d (%s).",
718 - p->target->name,
719 - p->parent->pid,
720 - p->parent->comm,
721 - p->pid,
722 - p->comm);
723 - }
724 -
731 found++;
732 }
733 }
728 -
729 - debug_log("TARGET INHERITANCE: merged %d processes", found);
734 }
735
732 - // init goes always to default target
733 - ebpf_pid_data_t *pid_entry = ebpf_find_or_create_pid_data(INIT_PID);
734 - pid_entry->target = apps_groups_default_target;
735 - // ebpf_pids[INIT_PID].target = apps_groups_default_target;
736 -
737 - // pid 0 goes always to default target
738 - pid_entry = ebpf_find_or_create_pid_data(0);
739 - pid_entry->target = apps_groups_default_target;
740 - //ebpf_pids[0].target = apps_groups_default_target;
741 -
742 - // give a default target on all top level processes
743 - if (unlikely(debug_enabled))
744 - loops++;
736 for (p = ebpf_pids_link_list; p; p = p->next) {
746 - // if the process is not merged itself
747 - // then is is a top level process
737 if (unlikely(!p->merged && !p->target))
738 p->target = apps_groups_default_target;
739
751 - // make sure all processes have a sortlist
740 if (unlikely(!p->sortlist))
741 p->sortlist = sortlist++;
742 }
743
756 - //ebpf_pids[1].sortlist = sortlist++;
744 pid_entry = ebpf_find_or_create_pid_data(1);
745 pid_entry->sortlist = sortlist++;
746
760 - // give a target to all merged child processes
747 found = 1;
748 while (found) {
763 - if (unlikely(debug_enabled))
764 - loops++;
749 found = 0;
750 for (p = ebpf_pids_link_list; p; p = p->next) {
751 if (unlikely(!p->target && p->merged && p->parent && p->parent->target)) {
752 p->target = p->parent->target;
753 found++;
770 -
771 - if (debug_enabled || (p->target && p->target->debug_enabled))
772 - debug_log_int(
773 - "TARGET INHERITANCE: %s is inherited by %d (%s) from its parent %d (%s) at phase 2.",
774 - p->target->name,
775 - p->pid,
776 - p->comm,
777 - p->parent->pid,
778 - p->parent->comm);
754 }
755 }
756 }
782 -
783 - debug_log("apply_apps_groups_targets_inheritance() made %d loops on the process tree", loops);
757 }
758
759 /**
@@ -792,6 +765,9 @@ static inline void post_aggregate_targets(struct ebpf_target *root)
765 {
766 struct ebpf_target *w;
767 for (w = root; w; w = w->next) {
768 + if (ebpf_plugin_stop())
769 + break;
770 +
771 if (w->collected_starttime) {
772 if (!w->starttime || w->collected_starttime < w->starttime) {
773 w->starttime = w->collected_starttime;
@@ -809,7 +785,6 @@ static inline void post_aggregate_targets(struct ebpf_target *root)
785 */
786 void ebpf_del_pid_entry(pid_t pid)
787 {
812 - //ebpf_pid_data_t *p = &ebpf_pids[pid];
788 ebpf_pid_data_t *p = ebpf_find_pid_data(pid);
789
790 debug_log("process %d %s exited, deleting it.", pid, p->comm);
@@ -845,7 +820,6 @@ void ebpf_del_pid_entry(pid_t pid)
820 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
821
822 freez(p);
848 - //memset(p, 0, sizeof(ebpf_pid_data_t));
823 ebpf_pid_del(pid);
824 }
825
@@ -879,6 +853,9 @@ static int ebpf_read_proc_filesystem()
853 struct dirent *de = NULL;
854
855 while ((de = readdir(dir))) {
856 + if (ebpf_plugin_stop())
857 + break;
858 +
859 char *endptr = de->d_name;
860
861 if (unlikely(de->d_type != DT_DIR || de->d_name[0] < '0' || de->d_name[0] > '9'))
@@ -932,6 +909,9 @@ void ebpf_parse_proc_files()
909 {
910 ebpf_pid_data_t *pids;
911 for (pids = ebpf_pids_link_list; pids;) {
912 + if (ebpf_plugin_stop())
913 + break;
914 +
915 if (kill(pids->pid, 0)) { // No PID found
916 ebpf_pid_data_t *next = pids->next;
917 ebpf_reset_specific_pid_data(pids);
@@ -954,8 +934,12 @@ void ebpf_parse_proc_files()
934
935 apps_groups_targets_count = zero_all_targets(apps_groups_root_target);
936
957 - for (pids = ebpf_pids_link_list; pids; pids = pids->next)
937 + for (pids = ebpf_pids_link_list; pids; pids = pids->next) {
938 + if (ebpf_plugin_stop())
939 + break;
940 +
941 aggregate_pid_on_target(pids->target, pids, NULL);
942 + }
943
944 ebpf_cleanup_exited_pids();
945 }
src/collectors/ebpf.plugin/ebpf_apps.h
+6 -8
@@ -40,8 +40,6 @@
40
41 #define EBPF_CLEANUP_FACTOR 2
42
43 -extern int pids_fd[NETDATA_EBPF_PIDS_END_IDX];
44 -
43 enum ebpf_main_index {
44 EBPF_MODULE_PROCESS_IDX,
45 EBPF_MODULE_SOCKET_IDX,
@@ -118,6 +116,10 @@ extern struct ebpf_target *users_root_target;
116 extern struct ebpf_target *groups_root_target;
117 extern uint64_t collect_pids;
118
119 +void ebpf_reset_pid_map_fds(void);
120 +void ebpf_set_pid_map_fd(int idx, int fd);
121 +int ebpf_get_pid_map_fd(int idx);
122 +
123 // ebpf_pid_data
124 typedef struct __attribute__((packed)) ebpf_pid_data {
125 uint32_t pid;
@@ -140,7 +142,7 @@ typedef struct __attribute__((packed)) ebpf_pid_data {
142
143 netdata_publish_fd_stat_t *fd;
144 netdata_publish_swap_t *swap;
143 - netdata_publish_shm_t *shm; // this has a leak issue
145 + netdata_publish_shm_t *shm;
146 netdata_publish_dcstat_t *dc;
147 netdata_publish_vfs_t *vfs;
148 netdata_publish_cachestat_t *cachestat;
@@ -149,7 +151,6 @@ typedef struct __attribute__((packed)) ebpf_pid_data {
151
152 } ebpf_pid_data_t;
153
152 -//extern ebpf_pid_data_t *ebpf_pids;
154 extern ebpf_pid_data_t *ebpf_pids_link_list;
155 extern size_t ebpf_all_pids_count;
156 extern size_t ebpf_hash_table_pids_count;
@@ -159,7 +160,6 @@ ebpf_pid_data_t *ebpf_find_or_create_pid_data(pid_t pid);
160
161 static inline ebpf_pid_data_t *ebpf_get_pid_data(uint32_t pid, uint32_t tgid, char *name, uint32_t idx)
162 {
162 - // ebpf_pid_data_t *ptr = &ebpf_pids[pid];
163 ebpf_pid_data_t *ptr = ebpf_find_or_create_pid_data(pid);
164 ptr->thread_collecting |= 1 << idx;
165 // The caller is getting data to work.
@@ -208,7 +208,7 @@ static inline void ebpf_reset_specific_pid_data(ebpf_pid_data_t *ptr)
208 continue;
209 }
210 // Check if we still have the map loaded
211 - int fd = pids_fd[idx];
211 + int fd = ebpf_get_pid_map_fd(idx);
212 if (fd <= STDERR_FILENO)
213 continue;
214
@@ -257,7 +257,6 @@ typedef struct ebpf_pid_stat {
257
258 uint32_t log_thrown;
259
260 - // char state;
260 uint32_t ppid;
261
262 int children_count; // number of processes directly referencing this
@@ -378,7 +377,6 @@ void ebpf_parse_proc_files();
377
378 // ARAL Section end
379
381 -// Threads integrated with apps
380 // Threads integrated with apps
381
382 #include "libnetdata/threads/threads.h"
src/collectors/ebpf.plugin/ebpf_cachestat.c
+341 -231
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_cachestat.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static char *cachestat_counter_dimension_name[NETDATA_CACHESTAT_END] = {"ratio", "dirty", "hit", "miss"};
8 static netdata_syscall_stat_t cachestat_counter_aggregated_data[NETDATA_CACHESTAT_END];
@@ -11,6 +12,7 @@ netdata_cachestat_pid_t *cachestat_vector = NULL;
12
13 static netdata_idx_t cachestat_hash_values[NETDATA_CACHESTAT_END];
14 static netdata_idx_t *cachestat_values = NULL;
15 +static bool cachestat_safe_clean = false;
16
17 ebpf_local_maps_t cachestat_maps[] = {
18 {.name = "cstat_global",
@@ -40,13 +42,13 @@ ebpf_local_maps_t cachestat_maps[] = {
42 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
43 #endif
44 },
43 - {
44 - .name = NULL,
45 - .internal_input = 0,
46 - .user_input = 0,
47 - .type = NETDATA_EBPF_MAP_CONTROLLER,
48 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
45 + {.name = NULL,
46 + .internal_input = 0,
47 + .user_input = 0,
48 + .type = NETDATA_EBPF_MAP_CONTROLLER,
49 + .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
50 #ifdef LIBBPF_MAJOR_VERSION
51 + .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
52 #endif
53 }};
54
@@ -55,7 +57,7 @@ struct config cachestat_config = APPCONFIG_INITIALIZER;
57 netdata_ebpf_targets_t cachestat_targets[] = {
58 {.name = "add_to_page_cache_lru", .mode = EBPF_LOAD_TRAMPOLINE},
59 {.name = "mark_page_accessed", .mode = EBPF_LOAD_TRAMPOLINE},
58 - {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE},
60 + {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}, // slot ACCOUNT_PAGE_DIRTIED: resolved dynamically at runtime
61 {.name = "mark_buffer_dirty", .mode = EBPF_LOAD_TRAMPOLINE},
62 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
63
@@ -64,6 +66,32 @@ static char *account_page[NETDATA_CACHESTAT_ACCOUNT_DIRTY_END] = {
66 "__set_page_dirty",
67 "__folio_mark_dirty"};
68
69 +static int cached_dirty_account_idx = -1;
70 +
71 +static inline void netdata_init_dirty_account_idx(void)
72 +{
73 + if (cached_dirty_account_idx != -1)
74 + return;
75 +
76 + if (!strcmp(
77 + cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
78 + account_page[NETDATA_CACHESTAT_FOLIO_DIRTY]))
79 + cached_dirty_account_idx = NETDATA_CACHESTAT_FOLIO_DIRTY;
80 + else if (!strcmp(
81 + cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
82 + account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY]))
83 + cached_dirty_account_idx = NETDATA_CACHESTAT_SET_PAGE_DIRTY;
84 + else
85 + cached_dirty_account_idx = NETDATA_CACHESTAT_ACCOUNT_PAGE_DIRTY;
86 +}
87 +
88 +static inline int netdata_get_dirty_account_idx(void)
89 +{
90 + if (cached_dirty_account_idx == -1)
91 + netdata_init_dirty_account_idx();
92 + return cached_dirty_account_idx;
93 +}
94 +
95 struct netdata_static_thread ebpf_read_cachestat = {
96 .name = "EBPF_READ_CACHESTAT",
97 .config_section = NULL,
@@ -101,14 +129,11 @@ static void ebpf_cachestat_disable_probe(struct cachestat_bpf *obj)
129 */
130 static void ebpf_cachestat_disable_specific_probe(struct cachestat_bpf *obj)
131 {
104 - if (!strcmp(
105 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
106 - account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
132 + int idx = netdata_get_dirty_account_idx();
133 + if (idx == NETDATA_CACHESTAT_FOLIO_DIRTY) {
134 bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_kprobe, false);
135 bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_kprobe, false);
109 - } else if (!strcmp(
110 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
111 - account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
136 + } else if (idx == NETDATA_CACHESTAT_SET_PAGE_DIRTY) {
137 bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_kprobe, false);
138 bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_kprobe, false);
139 } else {
@@ -143,14 +168,11 @@ static void ebpf_cachestat_disable_trampoline(struct cachestat_bpf *obj)
168 */
169 static void ebpf_cachestat_disable_specific_trampoline(struct cachestat_bpf *obj)
170 {
146 - if (!strcmp(
147 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
148 - account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
171 + int idx = netdata_get_dirty_account_idx();
172 + if (idx == NETDATA_CACHESTAT_FOLIO_DIRTY) {
173 bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_fentry, false);
174 bpf_program__set_autoload(obj->progs.netdata_set_page_dirty_fentry, false);
151 - } else if (!strcmp(
152 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
153 - account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
175 + } else if (idx == NETDATA_CACHESTAT_SET_PAGE_DIRTY) {
176 bpf_program__set_autoload(obj->progs.netdata_folio_mark_dirty_fentry, false);
177 bpf_program__set_autoload(obj->progs.netdata_account_page_dirtied_fentry, false);
178 } else {
@@ -176,25 +198,14 @@ static inline void netdata_set_trampoline_target(struct cachestat_bpf *obj)
198 bpf_program__set_attach_target(
199 obj->progs.netdata_mark_page_accessed_fentry, 0, cachestat_targets[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED].name);
200
179 - if (!strcmp(
180 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
181 - account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
182 - bpf_program__set_attach_target(
183 - obj->progs.netdata_folio_mark_dirty_fentry,
184 - 0,
185 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
186 - } else if (!strcmp(
187 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
188 - account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
189 - bpf_program__set_attach_target(
190 - obj->progs.netdata_set_page_dirty_fentry,
191 - 0,
192 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
201 + int idx = netdata_get_dirty_account_idx();
202 + const char *target_name = cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name;
203 + if (idx == NETDATA_CACHESTAT_FOLIO_DIRTY) {
204 + bpf_program__set_attach_target(obj->progs.netdata_folio_mark_dirty_fentry, 0, target_name);
205 + } else if (idx == NETDATA_CACHESTAT_SET_PAGE_DIRTY) {
206 + bpf_program__set_attach_target(obj->progs.netdata_set_page_dirty_fentry, 0, target_name);
207 } else {
194 - bpf_program__set_attach_target(
195 - obj->progs.netdata_account_page_dirtied_fentry,
196 - 0,
197 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
208 + bpf_program__set_attach_target(obj->progs.netdata_account_page_dirtied_fentry, 0, target_name);
209 }
210
211 bpf_program__set_attach_target(
@@ -228,27 +239,19 @@ static int ebpf_cachestat_attach_probe(struct cachestat_bpf *obj)
239 if (ret)
240 return -1;
241
231 - if (!strcmp(
232 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
233 - account_page[NETDATA_CACHESTAT_FOLIO_DIRTY])) {
234 - obj->links.netdata_folio_mark_dirty_kprobe = bpf_program__attach_kprobe(
235 - obj->progs.netdata_folio_mark_dirty_kprobe,
236 - false,
237 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
242 + int idx = netdata_get_dirty_account_idx();
243 + const char *target_name = cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name;
244 + if (idx == NETDATA_CACHESTAT_FOLIO_DIRTY) {
245 + obj->links.netdata_folio_mark_dirty_kprobe =
246 + bpf_program__attach_kprobe(obj->progs.netdata_folio_mark_dirty_kprobe, false, target_name);
247 ret = libbpf_get_error(obj->links.netdata_folio_mark_dirty_kprobe);
239 - } else if (!strcmp(
240 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name,
241 - account_page[NETDATA_CACHESTAT_SET_PAGE_DIRTY])) {
242 - obj->links.netdata_set_page_dirty_kprobe = bpf_program__attach_kprobe(
243 - obj->progs.netdata_set_page_dirty_kprobe,
244 - false,
245 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
248 + } else if (idx == NETDATA_CACHESTAT_SET_PAGE_DIRTY) {
249 + obj->links.netdata_set_page_dirty_kprobe =
250 + bpf_program__attach_kprobe(obj->progs.netdata_set_page_dirty_kprobe, false, target_name);
251 ret = libbpf_get_error(obj->links.netdata_set_page_dirty_kprobe);
252 } else {
248 - obj->links.netdata_account_page_dirtied_kprobe = bpf_program__attach_kprobe(
249 - obj->progs.netdata_account_page_dirtied_kprobe,
250 - false,
251 - cachestat_targets[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED].name);
253 + obj->links.netdata_account_page_dirtied_kprobe =
254 + bpf_program__attach_kprobe(obj->progs.netdata_account_page_dirtied_kprobe, false, target_name);
255 ret = libbpf_get_error(obj->links.netdata_account_page_dirtied_kprobe);
256 }
257
@@ -389,7 +392,7 @@ static void ebpf_obsolete_cachestat_services(ebpf_module_t *em, char *id)
392 EBPF_CACHESTAT_UNITS_HITS,
393 NETDATA_CACHESTAT_SUBMENU,
394 NETDATA_EBPF_CHART_TYPE_LINE,
392 - NETDATA_SYSTEMD_CACHESTAT_HIT_FILE_CONTEXT,
395 + NETDATA_SYSTEMD_CACHESTAT_HIT_FILES_CONTEXT,
396 21102,
397 em->update_every);
398
@@ -563,13 +566,35 @@ void ebpf_obsolete_cachestat_apps_charts(struct ebpf_module *em)
566 *
567 * @param ptr thread data.
568 */
569 +void ebpf_cachestat_unload_bpf(ebpf_module_t *em)
570 +{
571 +#ifdef LIBBPF_MAJOR_VERSION
572 + if (cachestat_bpf_obj) {
573 + cachestat_bpf__destroy(cachestat_bpf_obj);
574 + cachestat_bpf_obj = NULL;
575 + }
576 +#endif
577 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
578 + ebpf_unload_legacy_code(em->objects, em->probe_links);
579 + em->objects = NULL;
580 + em->probe_links = NULL;
581 + }
582 +}
583 +
584 static void ebpf_cachestat_exit(void *pptr)
585 {
568 - pids_fd[NETDATA_EBPF_PIDS_CACHESTAT_IDX] = -1;
586 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_CACHESTAT_IDX, -1);
587 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
588 if (!em)
589 return;
590
591 + if (!cachestat_safe_clean) {
592 + netdata_mutex_lock(&ebpf_exit_cleanup);
593 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
594 + netdata_mutex_unlock(&ebpf_exit_cleanup);
595 + return;
596 + }
597 +
598 netdata_mutex_lock(&lock);
599 collect_pids &= ~(1 << EBPF_MODULE_CACHESTAT_IDX);
600 netdata_mutex_unlock(&lock);
@@ -577,7 +602,7 @@ static void ebpf_cachestat_exit(void *pptr)
602 if (ebpf_read_cachestat.thread)
603 nd_thread_signal_cancel(ebpf_read_cachestat.thread);
604
580 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
605 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
606 netdata_mutex_lock(&lock);
607 if (em->cgroup_charts) {
608 ebpf_obsolete_cachestat_cgroup_charts(em);
@@ -594,25 +619,17 @@ static void ebpf_cachestat_exit(void *pptr)
619 netdata_mutex_unlock(&lock);
620 }
621
597 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
598 -
599 -#ifdef LIBBPF_MAJOR_VERSION
600 - if (cachestat_bpf_obj) {
601 - cachestat_bpf__destroy(cachestat_bpf_obj);
602 - cachestat_bpf_obj = NULL;
603 - }
604 -#endif
605 -
606 - if (em->objects) {
607 - ebpf_unload_legacy_code(em->objects, em->probe_links);
608 - em->objects = NULL;
609 - em->probe_links = NULL;
610 - }
622 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
623 + em->functions.bpf_unload(em);
624
625 netdata_mutex_lock(&ebpf_exit_cleanup);
626 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
614 - ebpf_update_stats(&plugin_statistics, em);
627 netdata_mutex_unlock(&ebpf_exit_cleanup);
628 +
629 + freez(cachestat_vector);
630 + cachestat_vector = NULL;
631 + freez(cachestat_values);
632 + cachestat_values = NULL;
633 }
634
635 /*****************************************************************
@@ -632,14 +649,15 @@ static void ebpf_cachestat_exit(void *pptr)
649 * @param apcl calls for add_to_page_cache_lru during the last second.
650 * @param apd calls for account_page_dirtied during the last second.
651 */
635 -void cachestat_update_publish(netdata_publish_cachestat_t *out, uint64_t mpa, uint64_t mbd, uint64_t apcl, uint64_t apd)
652 +static void
653 +cachestat_update_publish(netdata_publish_cachestat_t *out, uint64_t mpa, uint64_t mbd, uint64_t apcl, uint64_t apd)
654 {
655 // Adapted algorithm from https://github.com/iovisor/bcc/blob/master/tools/cachestat.py#L126-L138
638 - NETDATA_DOUBLE total = (NETDATA_DOUBLE)(((long long)mpa) - ((long long)mbd));
656 + NETDATA_DOUBLE total = (NETDATA_DOUBLE)mpa - (NETDATA_DOUBLE)mbd;
657 if (total < 0)
658 total = 0;
659
642 - NETDATA_DOUBLE misses = (NETDATA_DOUBLE)(((long long)apcl) - ((long long)apd));
660 + NETDATA_DOUBLE misses = (NETDATA_DOUBLE)apcl - (NETDATA_DOUBLE)apd;
661 if (misses < 0)
662 misses = 0;
663
@@ -659,13 +677,47 @@ void cachestat_update_publish(netdata_publish_cachestat_t *out, uint64_t mpa, ui
677 }
678
679 /**
662 - * Save previous values
680 + * Calculate cachestat from current and previous values
681 + *
682 + * Calculate delta values and update publish structure.
683 + *
684 + * @param out structure that will receive data.
685 + * @param current pointer to current cache statistics.
686 + * @param prev pointer to previous cache statistics.
687 + */
688 +static void cachestat_calculate_from_values(
689 + netdata_publish_cachestat_t *out,
690 + const netdata_cachestat_t *current,
691 + const netdata_cachestat_t *prev)
692 +{
693 + int64_t mpa = (int64_t)current->mark_page_accessed - (int64_t)prev->mark_page_accessed;
694 + if (mpa < 0)
695 + mpa = 0;
696 +
697 + int64_t mbd = (int64_t)current->mark_buffer_dirty - (int64_t)prev->mark_buffer_dirty;
698 + if (mbd < 0)
699 + mbd = 0;
700 +
701 + int64_t apcl = (int64_t)current->add_to_page_cache_lru - (int64_t)prev->add_to_page_cache_lru;
702 + if (apcl < 0)
703 + apcl = 0;
704 +
705 + int64_t apd = (int64_t)current->account_page_dirtied - (int64_t)prev->account_page_dirtied;
706 + if (apd < 0)
707 + apd = 0;
708 +
709 + out->dirty = (long long)mbd;
710 + cachestat_update_publish(out, mpa, mbd, apcl, apd);
711 +}
712 +
713 +/**
714 + * Initialize cachestat
715 *
664 - * Save values used this time.
716 + * Initialize prev values on first call.
717 *
666 - * @param publish
718 + * @param publish the structure where we will store the data.
719 */
668 -static void save_previous_values(netdata_publish_cachestat_t *publish)
720 +static void cachestat_initialize(netdata_publish_cachestat_t *publish)
721 {
722 publish->prev.mark_page_accessed = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED];
723 publish->prev.account_page_dirtied = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED];
@@ -680,21 +732,24 @@ static void save_previous_values(netdata_publish_cachestat_t *publish)
732 */
733 static void calculate_stats(netdata_publish_cachestat_t *publish)
734 {
683 - if (!publish->prev.mark_page_accessed) {
684 - save_previous_values(publish);
735 + if (!publish->prev.mark_page_accessed && !publish->prev.add_to_page_cache_lru && !publish->prev.mark_buffer_dirty &&
736 + !publish->prev.account_page_dirtied) {
737 + cachestat_initialize(publish);
738 return;
739 }
740
688 - uint64_t mpa = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED] - publish->prev.mark_page_accessed;
689 - uint64_t mbd = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY] - publish->prev.mark_buffer_dirty;
690 - uint64_t apcl =
691 - cachestat_hash_values[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU] - publish->prev.add_to_page_cache_lru;
692 - uint64_t apd = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED] - publish->prev.account_page_dirtied;
741 + netdata_cachestat_t current = {
742 + .mark_page_accessed = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_PAGE_ACCESSED],
743 + .mark_buffer_dirty = cachestat_hash_values[NETDATA_KEY_CALLS_MARK_BUFFER_DIRTY],
744 + .add_to_page_cache_lru = cachestat_hash_values[NETDATA_KEY_CALLS_ADD_TO_PAGE_CACHE_LRU],
745 + .account_page_dirtied = cachestat_hash_values[NETDATA_KEY_CALLS_ACCOUNT_PAGE_DIRTIED]};
746
694 - save_previous_values(publish);
747 + cachestat_calculate_from_values(publish, &current, &publish->prev);
748
696 - // We are changing the original algorithm to have a smooth ratio.
697 - cachestat_update_publish(publish, mpa, mbd, apcl, apd);
749 + publish->prev.mark_page_accessed = current.mark_page_accessed;
750 + publish->prev.account_page_dirtied = current.account_page_dirtied;
751 + publish->prev.add_to_page_cache_lru = current.add_to_page_cache_lru;
752 + publish->prev.mark_buffer_dirty = current.mark_buffer_dirty;
753 }
754
755 /*****************************************************************
@@ -715,20 +770,21 @@ static void cachestat_apps_accumulator(netdata_cachestat_pid_t *out, int maps_pe
770 {
771 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
772 netdata_cachestat_pid_t *total = &out[0];
718 - uint64_t ct = total->ct;
773 for (i = 1; i < end; i++) {
774 + if (ebpf_plugin_stop())
775 + break;
776 +
777 netdata_cachestat_pid_t *w = &out[i];
778 total->account_page_dirtied += w->account_page_dirtied;
779 total->add_to_page_cache_lru += w->add_to_page_cache_lru;
780 total->mark_buffer_dirty += w->mark_buffer_dirty;
781 total->mark_page_accessed += w->mark_page_accessed;
725 - if (w->ct > ct)
726 - ct = w->ct;
782 + if (w->ct > total->ct)
783 + total->ct = w->ct;
784
785 if (!total->name[0] && w->name[0])
729 - strncpyz(total->name, w->name, sizeof(total->name) - 1);
786 + strncpyz(total->name, w->name, sizeof(total->name));
787 }
731 - total->ct = ct;
788 }
789
790 /**
@@ -742,9 +798,7 @@ static void cachestat_apps_accumulator(netdata_cachestat_pid_t *out, int maps_pe
798 static inline void cachestat_save_pid_values(netdata_publish_cachestat_t *out, netdata_cachestat_pid_t *in)
799 {
800 out->ct = in->ct;
745 - if (out->current.mark_page_accessed) {
746 - memcpy(&out->prev, &out->current, sizeof(netdata_cachestat_t));
747 - }
801 + memcpy(&out->prev, &out->current, sizeof(netdata_cachestat_t));
802
803 out->current.account_page_dirtied = in[0].account_page_dirtied;
804 out->current.add_to_page_cache_lru = in[0].add_to_page_cache_lru;
@@ -769,6 +823,9 @@ static void ebpf_read_cachestat_apps_table(int maps_per_core)
823
824 uint32_t key = 0, next_key = 0;
825 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
826 + if (ebpf_plugin_stop())
827 + break;
828 +
829 if (bpf_map_lookup_elem(fd, &key, cv)) {
830 goto end_cachestat_loop;
831 }
@@ -808,8 +865,14 @@ static void ebpf_update_cachestat_cgroup()
865 ebpf_cgroup_target_t *ect;
866 netdata_mutex_lock(&mutex_cgroup_shm);
867 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
868 + if (ebpf_plugin_stop())
869 + break;
870 +
871 struct pid_on_target2 *pids;
872 for (pids = ect->pids; pids; pids = pids->next) {
873 + if (ebpf_plugin_stop())
874 + break;
875 +
876 uint32_t pid = pids->pid;
877 netdata_publish_cachestat_t *out = &pids->cachestat;
878
@@ -825,33 +888,94 @@ static void ebpf_update_cachestat_cgroup()
888 netdata_mutex_unlock(&mutex_cgroup_shm);
889 }
890
828 -/**
829 - * Cachestat sum PIDs
830 - *
831 - * Sum values for all PIDs associated to a group
832 - *
833 - * @param publish output structure.
834 - * @param root structure with listed IPs
835 - */
836 -void ebpf_cachestat_sum_pids(netdata_publish_cachestat_t *publish, struct ebpf_pid_on_target *root)
891 +static inline void sum_single_pid_cachestat(netdata_cachestat_t *dst, const netdata_cachestat_t *src)
892 {
838 - memcpy(&publish->prev, &publish->current, sizeof(publish->current));
839 - memset(&publish->current, 0, sizeof(publish->current));
893 + dst->account_page_dirtied += src->account_page_dirtied;
894 + dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
895 + dst->mark_buffer_dirty += src->mark_buffer_dirty;
896 + dst->mark_page_accessed += src->mark_page_accessed;
897 +}
898
899 +static void cachestat_sum_pids_internal(netdata_publish_cachestat_t *publish, void *root, bool is_cgroup)
900 +{
901 + netdata_cachestat_t new_prev = publish->current;
902 + memset(&publish->current, 0, sizeof(publish->current));
903 netdata_cachestat_t *dst = &publish->current;
842 - for (; root; root = root->next) {
843 - uint32_t pid = root->pid;
844 - netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_get_shm_pointer_unsafe(pid, NETDATA_EBPF_PIDS_CACHESTAT_IDX);
845 - if (!local_pid)
846 - continue;
847 - netdata_publish_cachestat_t *w = &local_pid->cachestat;
904
849 - netdata_cachestat_t *src = &w->current;
850 - dst->account_page_dirtied += src->account_page_dirtied;
851 - dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
852 - dst->mark_buffer_dirty += src->mark_buffer_dirty;
853 - dst->mark_page_accessed += src->mark_page_accessed;
905 + if (is_cgroup) {
906 + struct pid_on_target2 *r = (struct pid_on_target2 *)root;
907 + for (; r; r = r->next) {
908 + if (ebpf_plugin_stop())
909 + break;
910 + sum_single_pid_cachestat(dst, &r->cachestat.current);
911 + }
912 + } else {
913 + struct ebpf_pid_on_target *r = (struct ebpf_pid_on_target *)root;
914 + for (; r; r = r->next) {
915 + if (ebpf_plugin_stop())
916 + break;
917 +
918 + uint32_t pid = r->pid;
919 + netdata_ebpf_pid_stats_t *local_pid =
920 + netdata_ebpf_get_shm_pointer_unsafe(pid, NETDATA_EBPF_PIDS_CACHESTAT_IDX);
921 + if (!local_pid)
922 + continue;
923 + netdata_publish_cachestat_t *w = &local_pid->cachestat;
924 + sum_single_pid_cachestat(dst, &w->current);
925 + }
926 + }
927 + publish->prev = new_prev;
928 +}
929 +
930 +static void write_cachestat_charts(
931 + const char *family,
932 + const char *name,
933 + const netdata_publish_cachestat_t *npc,
934 + const char *ratio_name,
935 + const char *dirty_name,
936 + const char *hit_name,
937 + const char *miss_name)
938 +{
939 + if (!ratio_name) {
940 + /* app charts use the new-style naming from ebpf_cachestat_create_apps_charts() */
941 + ebpf_write_begin_chart(family, name, "_ebpf_cachestat_hit_ratio");
942 + write_chart_dimension("ratio", (long long)npc->ratio);
943 + ebpf_write_end_chart();
944 +
945 + ebpf_write_begin_chart(family, name, "_ebpf_cachestat_dirty_pages");
946 + write_chart_dimension("pages", (long long)npc->dirty);
947 + ebpf_write_end_chart();
948 +
949 + ebpf_write_begin_chart(family, name, "_ebpf_cachestat_access");
950 + write_chart_dimension("hits", (long long)npc->hit);
951 + ebpf_write_end_chart();
952 +
953 + ebpf_write_begin_chart(family, name, "_ebpf_cachestat_misses");
954 + write_chart_dimension("misses", (long long)npc->miss);
955 + ebpf_write_end_chart();
956 + return;
957 }
958 +
959 + ebpf_write_begin_chart(family, name, NETDATA_CACHESTAT_HIT_RATIO_CHART);
960 + write_chart_dimension(ratio_name, (long long)npc->ratio);
961 + ebpf_write_end_chart();
962 +
963 + ebpf_write_begin_chart(family, name, NETDATA_CACHESTAT_DIRTY_CHART);
964 + write_chart_dimension(dirty_name, (long long)npc->dirty);
965 + ebpf_write_end_chart();
966 +
967 + ebpf_write_begin_chart(family, name, NETDATA_CACHESTAT_HIT_CHART);
968 + write_chart_dimension(hit_name, (long long)npc->hit);
969 + ebpf_write_end_chart();
970 +
971 + ebpf_write_begin_chart(family, name, NETDATA_CACHESTAT_MISSES_CHART);
972 + write_chart_dimension(miss_name, (long long)npc->miss);
973 + ebpf_write_end_chart();
974 +}
975 +
976 +void ebpf_cachestat_sum_pids(netdata_publish_cachestat_t *publish, struct ebpf_pid_on_target *root)
977 +{
978 + cachestat_sum_pids_internal(publish, root, false);
979 }
980
981 /**
@@ -863,6 +987,9 @@ void ebpf_cachestat_resume_apps_data()
987
988 netdata_mutex_lock(&collect_data_mutex);
989 for (w = apps_groups_root_target; w; w = w->next) {
990 + if (ebpf_plugin_stop())
991 + break;
992 +
993 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_CACHESTAT_IDX))))
994 continue;
995
@@ -895,29 +1022,51 @@ void ebpf_read_cachestat_thread(void *ptr)
1022
1023 uint32_t lifetime = em->lifetime;
1024 uint32_t running_time = 0;
898 - pids_fd[NETDATA_EBPF_PIDS_CACHESTAT_IDX] = cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd;
1025 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_CACHESTAT_IDX, cachestat_maps[NETDATA_CACHESTAT_PID_STATS].map_fd);
1026 heartbeat_t hb;
900 - heartbeat_init(&hb, update_every * USEC_PER_SEC);
1027 + heartbeat_init(&hb, USEC_PER_SEC);
1028 while (!ebpf_plugin_stop() && running_time < lifetime) {
1029 + if (ebpf_plugin_stop())
1030 + break;
1031 +
1032 (void)heartbeat_next(&hb);
903 - if (ebpf_plugin_stop() || ++counter != update_every)
1033 + if (ebpf_plugin_stop())
1034 + break;
1035 +
1036 + if (++counter != update_every)
1037 continue;
1038
906 - sem_wait(shm_mutex_ebpf_integration);
1039 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
1040 + if (errno != ECANCELED)
1041 + netdata_log_error("CACHESTAT: Failed to wait on semaphore.");
1042 + break;
1043 + }
1044 +
1045 ebpf_read_cachestat_apps_table(maps_per_core);
1046 ebpf_cachestat_resume_apps_data();
1047 + if (ebpf_plugin_stop()) {
1048 + if (sem_post(shm_mutex_ebpf_integration))
1049 + netdata_log_error("CACHESTAT: Failed to post semaphore.");
1050 + break;
1051 + }
1052 +
1053 if (cgroups && shm_ebpf_cgroup.header)
1054 ebpf_update_cachestat_cgroup();
911 - sem_post(shm_mutex_ebpf_integration);
1055 + if (sem_post(shm_mutex_ebpf_integration)) {
1056 + netdata_log_error("CACHESTAT: Failed to post semaphore.");
1057 + break;
1058 + }
1059
1060 counter = 0;
1061
1062 + if (ebpf_plugin_stop())
1063 + break;
1064 +
1065 netdata_mutex_lock(&ebpf_exit_cleanup);
916 - if (running_time && !em->running_time)
917 - running_time = update_every;
918 - else
1066 + if (running_time)
1067 running_time += update_every;
920 -
1068 + else
1069 + running_time = update_every;
1070 em->running_time = running_time;
1071 netdata_mutex_unlock(&ebpf_exit_cleanup);
1072 }
@@ -936,6 +1085,9 @@ void ebpf_cachestat_create_apps_charts(struct ebpf_module *em, void *ptr)
1085 struct ebpf_target *w;
1086 int update_every = em->update_every;
1087 for (w = root; w; w = w->next) {
1088 + if (ebpf_plugin_stop())
1089 + break;
1090 +
1091 if (unlikely(!w->exposed))
1092 continue;
1093
@@ -1077,73 +1229,28 @@ static void cachestat_send_global(netdata_publish_cachestat_t *publish)
1229 * Send data to Netdata calling auxiliary functions.
1230 *
1231 * @param root the target list.
1080 -*/
1232 + */
1233 void ebpf_cache_send_apps_data(struct ebpf_target *root)
1234 {
1235 struct ebpf_target *w;
1084 - collected_number value;
1236
1237 netdata_mutex_lock(&collect_data_mutex);
1238 for (w = root; w; w = w->next) {
1239 + if (ebpf_plugin_stop())
1240 + break;
1241 +
1242 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_CACHESTAT_IDX))))
1243 continue;
1244
1091 - netdata_cachestat_t *current = &w->cachestat.current;
1092 - netdata_cachestat_t *prev = &w->cachestat.prev;
1093 -
1094 - uint64_t mpa = current->mark_page_accessed - prev->mark_page_accessed;
1095 - uint64_t mbd = current->mark_buffer_dirty - prev->mark_buffer_dirty;
1096 - w->cachestat.dirty = (long long)mbd;
1097 - uint64_t apcl = current->add_to_page_cache_lru - prev->add_to_page_cache_lru;
1098 - uint64_t apd = current->account_page_dirtied - prev->account_page_dirtied;
1099 -
1100 - cachestat_update_publish(&w->cachestat, mpa, mbd, apcl, apd);
1101 -
1102 - value = (collected_number)w->cachestat.ratio;
1103 - ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_cachestat_hit_ratio");
1104 - write_chart_dimension("ratio", value);
1105 - ebpf_write_end_chart();
1106 -
1107 - value = (collected_number)w->cachestat.dirty;
1108 - ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_cachestat_dirty_pages");
1109 - write_chart_dimension("pages", value);
1110 - ebpf_write_end_chart();
1111 -
1112 - value = (collected_number)w->cachestat.hit;
1113 - ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_cachestat_access");
1114 - write_chart_dimension("hits", value);
1115 - ebpf_write_end_chart();
1116 -
1117 - value = (collected_number)w->cachestat.miss;
1118 - ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_ebpf_cachestat_misses");
1119 - write_chart_dimension("misses", value);
1120 - ebpf_write_end_chart();
1245 + cachestat_calculate_from_values(&w->cachestat, &w->cachestat.current, &w->cachestat.prev);
1246 + write_cachestat_charts(NETDATA_APP_FAMILY, w->clean_name, &w->cachestat, NULL, NULL, NULL, NULL);
1247 }
1248 netdata_mutex_unlock(&collect_data_mutex);
1249 }
1250
1125 -/**
1126 - * Cachestat sum PIDs
1127 - *
1128 - * Sum values for all PIDs associated to a group
1129 - *
1130 - * @param publish output structure.
1131 - * @param root structure with listed IPs
1132 - */
1251 void ebpf_cachestat_sum_cgroup_pids(netdata_publish_cachestat_t *publish, struct pid_on_target2 *root)
1252 {
1135 - memcpy(&publish->prev, &publish->current, sizeof(publish->current));
1136 - memset(&publish->current, 0, sizeof(publish->current));
1137 -
1138 - netdata_cachestat_t *dst = &publish->current;
1139 - for (; root; root = root->next) {
1140 - netdata_cachestat_t *src = &root->cachestat.current;
1141 -
1142 - dst->account_page_dirtied += src->account_page_dirtied;
1143 - dst->add_to_page_cache_lru += src->add_to_page_cache_lru;
1144 - dst->mark_buffer_dirty += src->mark_buffer_dirty;
1145 - dst->mark_page_accessed += src->mark_page_accessed;
1146 - }
1253 + cachestat_sum_pids_internal(publish, root, true);
1254 }
1255
1256 /**
@@ -1155,18 +1262,12 @@ void ebpf_cachestat_calc_chart_values()
1262 {
1263 ebpf_cgroup_target_t *ect;
1264 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1158 - ebpf_cachestat_sum_cgroup_pids(&ect->publish_cachestat, ect->pids);
1159 -
1160 - netdata_cachestat_t *current = &ect->publish_cachestat.current;
1161 - netdata_cachestat_t *prev = &ect->publish_cachestat.prev;
1162 -
1163 - uint64_t mpa = current->mark_page_accessed - prev->mark_page_accessed;
1164 - uint64_t mbd = current->mark_buffer_dirty - prev->mark_buffer_dirty;
1165 - ect->publish_cachestat.dirty = (long long)mbd;
1166 - uint64_t apcl = current->add_to_page_cache_lru - prev->add_to_page_cache_lru;
1167 - uint64_t apd = current->account_page_dirtied - prev->account_page_dirtied;
1265 + if (ebpf_plugin_stop())
1266 + break;
1267
1169 - cachestat_update_publish(&ect->publish_cachestat, mpa, mbd, apcl, apd);
1268 + ebpf_cachestat_sum_cgroup_pids(&ect->publish_cachestat, ect->pids);
1269 + cachestat_calculate_from_values(
1270 + &ect->publish_cachestat, &ect->publish_cachestat.current, &ect->publish_cachestat.prev);
1271 }
1272 }
1273
@@ -1212,7 +1313,7 @@ static void ebpf_create_systemd_cachestat_charts(int update_every)
1313 .charttype = NETDATA_EBPF_CHART_TYPE_LINE,
1314 .order = 21102,
1315 .algorithm = EBPF_CHART_ALGORITHM_ABSOLUTE,
1215 - .context = NETDATA_SYSTEMD_CACHESTAT_HIT_FILE_CONTEXT,
1316 + .context = NETDATA_SYSTEMD_CACHESTAT_HIT_FILES_CONTEXT,
1317 .module = NETDATA_EBPF_MODULE_NAME_CACHESTAT,
1318 .update_every = 0,
1319 .suffix = NETDATA_CACHESTAT_HIT_CHART,
@@ -1237,6 +1338,9 @@ static void ebpf_create_systemd_cachestat_charts(int update_every)
1338
1339 ebpf_cgroup_target_t *w;
1340 for (w = ebpf_cgroup_pids; w; w = w->next) {
1341 + if (ebpf_plugin_stop())
1342 + break;
1343 +
1344 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_CACHESTAT_CHART))
1345 continue;
1346
@@ -1263,25 +1367,14 @@ static void ebpf_send_systemd_cachestat_charts()
1367 ebpf_cgroup_target_t *ect;
1368
1369 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1370 + if (ebpf_plugin_stop())
1371 + break;
1372 +
1373 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_CACHESTAT_CHART))) {
1374 continue;
1375 }
1376
1270 - ebpf_write_begin_chart(ect->name, NETDATA_CACHESTAT_HIT_RATIO_CHART, "");
1271 - write_chart_dimension("percentage", (long long)ect->publish_cachestat.ratio);
1272 - ebpf_write_end_chart();
1273 -
1274 - ebpf_write_begin_chart(ect->name, NETDATA_CACHESTAT_DIRTY_CHART, "");
1275 - write_chart_dimension("pages", (long long)ect->publish_cachestat.dirty);
1276 - ebpf_write_end_chart();
1277 -
1278 - ebpf_write_begin_chart(ect->name, NETDATA_CACHESTAT_HIT_CHART, "");
1279 - write_chart_dimension("hits", (long long)ect->publish_cachestat.hit);
1280 - ebpf_write_end_chart();
1281 -
1282 - ebpf_write_begin_chart(ect->name, NETDATA_CACHESTAT_MISSES_CHART, "");
1283 - write_chart_dimension("misses", (long long)ect->publish_cachestat.miss);
1284 - ebpf_write_end_chart();
1377 + write_cachestat_charts(ect->name, "", &ect->publish_cachestat, "percentage", "pages", "hits", "misses");
1378 }
1379 }
1380
@@ -1292,23 +1385,14 @@ static void ebpf_send_systemd_cachestat_charts()
1385 */
1386 static void ebpf_send_specific_cachestat_data(char *type, netdata_publish_cachestat_t *npc)
1387 {
1295 - ebpf_write_begin_chart(type, NETDATA_CACHESTAT_HIT_RATIO_CHART, "");
1296 - write_chart_dimension(
1297 - cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_RATIO].name, (long long)npc->ratio);
1298 - ebpf_write_end_chart();
1299 -
1300 - ebpf_write_begin_chart(type, NETDATA_CACHESTAT_DIRTY_CHART, "");
1301 - write_chart_dimension(
1302 - cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY].name, (long long)npc->dirty);
1303 - ebpf_write_end_chart();
1304 -
1305 - ebpf_write_begin_chart(type, NETDATA_CACHESTAT_HIT_CHART, "");
1306 - write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT].name, (long long)npc->hit);
1307 - ebpf_write_end_chart();
1308 -
1309 - ebpf_write_begin_chart(type, NETDATA_CACHESTAT_MISSES_CHART, "");
1310 - write_chart_dimension(cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS].name, (long long)npc->miss);
1311 - ebpf_write_end_chart();
1388 + write_cachestat_charts(
1389 + type,
1390 + "",
1391 + npc,
1392 + cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_RATIO].name,
1393 + cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_DIRTY].name,
1394 + cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_HIT].name,
1395 + cachestat_counter_publish_aggregated[NETDATA_CACHESTAT_IDX_MISS].name);
1396 }
1397
1398 /**
@@ -1470,6 +1554,9 @@ void ebpf_cachestat_send_cgroup_data(int update_every)
1554 }
1555
1556 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1557 + if (ebpf_plugin_stop())
1558 + break;
1559 +
1560 if (ect->systemd)
1561 continue;
1562
@@ -1512,13 +1599,19 @@ static void cachestat_collector(ebpf_module_t *em)
1599 while (!ebpf_plugin_stop() && running_time < lifetime) {
1600 (void)heartbeat_next(&hb);
1601
1515 - if (ebpf_plugin_stop() || ++counter != update_every)
1602 + if (ebpf_plugin_stop())
1603 + break;
1604 +
1605 + if (++counter != update_every)
1606 continue;
1607
1608 counter = 0;
1609 netdata_apps_integration_flags_t apps = em->apps_charts;
1610 ebpf_cachestat_read_global_tables(stats, maps_per_core);
1611
1612 + if (ebpf_plugin_stop())
1613 + break;
1614 +
1615 netdata_mutex_lock(&lock);
1616
1617 cachestat_send_global(&publish);
@@ -1526,17 +1619,24 @@ static void cachestat_collector(ebpf_module_t *em)
1619 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
1620 ebpf_cache_send_apps_data(apps_groups_root_target);
1621
1622 + if (ebpf_plugin_stop()) {
1623 + netdata_mutex_unlock(&lock);
1624 + break;
1625 + }
1626 +
1627 if (cgroups && shm_ebpf_cgroup.header)
1628 ebpf_cachestat_send_cgroup_data(update_every);
1629
1630 netdata_mutex_unlock(&lock);
1631
1632 + if (ebpf_plugin_stop())
1633 + break;
1634 +
1635 netdata_mutex_lock(&ebpf_exit_cleanup);
1535 - if (running_time && !em->running_time)
1536 - running_time = update_every;
1537 - else
1636 + if (running_time)
1637 running_time += update_every;
1539 -
1638 + else
1639 + running_time = update_every;
1640 em->running_time = running_time;
1641 netdata_mutex_unlock(&ebpf_exit_cleanup);
1642 }
@@ -1696,8 +1796,13 @@ static int ebpf_cachestat_load_bpf(ebpf_module_t *em)
1796 cachestat_bpf_obj = cachestat_bpf__open();
1797 if (!cachestat_bpf_obj)
1798 ret = -1;
1699 - else
1799 + else {
1800 ret = ebpf_cachestat_load_and_attach(cachestat_bpf_obj, em);
1801 + if (ret) {
1802 + cachestat_bpf__destroy(cachestat_bpf_obj);
1803 + cachestat_bpf_obj = NULL;
1804 + }
1805 + }
1806 }
1807 #endif
1808
@@ -1722,6 +1827,10 @@ void ebpf_cachestat_thread(void *ptr)
1827
1828 CLEANUP_FUNCTION_REGISTER(ebpf_cachestat_exit) cleanup_ptr = em;
1829
1830 + if (!ebpf_module_thread_has_valid_state(em)) {
1831 + goto endcachestat;
1832 + }
1833 +
1834 em->maps = cachestat_maps;
1835
1836 ebpf_update_pid_table(&cachestat_maps[NETDATA_CACHESTAT_PID_STATS], em);
@@ -1760,6 +1869,7 @@ void ebpf_cachestat_thread(void *ptr)
1869 ebpf_read_cachestat.thread =
1870 nd_thread_create(ebpf_read_cachestat.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_cachestat_thread, em);
1871
1872 + cachestat_safe_clean = true;
1873 cachestat_collector(em);
1874
1875 endcachestat:
src/collectors/ebpf.plugin/ebpf_cachestat.h
+1 -1
@@ -36,7 +36,7 @@
36
37 #define NETDATA_SYSTEMD_CACHESTAT_HIT_RATIO_CONTEXT "systemd.service.cachestat_ratio"
38 #define NETDATA_SYSTEMD_CACHESTAT_MODIFIED_CACHE_CONTEXT "systemd.service.cachestat_dirties"
39 -#define NETDATA_SYSTEMD_CACHESTAT_HIT_FILE_CONTEXT "systemd.service.cachestat_hits"
39 +#define NETDATA_SYSTEMD_CACHESTAT_HIT_FILES_CONTEXT "systemd.service.cachestat_hits"
40 #define NETDATA_SYSTEMD_CACHESTAT_MISS_FILES_CONTEXT "systemd.service.cachestat_misses"
41
42 // variables
src/collectors/ebpf.plugin/ebpf_cgroup.c
+48 -34
@@ -4,6 +4,7 @@
4
5 #include "ebpf.h"
6 #include "ebpf_cgroup.h"
7 +#include "libbpf_api/ebpf_library.h"
8
9 ebpf_cgroup_target_t *ebpf_cgroup_pids = NULL;
10 static void *ebpf_mapped_memory = NULL;
@@ -24,10 +25,8 @@ int send_cgroup_chart = 0;
25 */
26 static inline void *ebpf_cgroup_map_shm_locally(int fd, size_t length)
27 {
27 - void *value;
28 -
29 - value = nd_mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
30 - if (!value) {
28 + void *value = nd_mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
29 + if (value == MAP_FAILED) {
30 netdata_log_error(
31 "Cannot map shared memory used between eBPF and cgroup, integration between processes won't happen");
32 close(shm_fd_ebpf_cgroup);
@@ -81,7 +80,7 @@ void ebpf_map_cgroup_shared_memory()
80 // Map only header
81 void *mapped = (netdata_ebpf_cgroup_shm_header_t *)ebpf_cgroup_map_shm_locally(
82 shm_fd_ebpf_cgroup, sizeof(netdata_ebpf_cgroup_shm_header_t));
84 - if (unlikely(mapped == SEM_FAILED)) {
83 + if (unlikely(mapped == MAP_FAILED)) {
84 return;
85 }
86 netdata_ebpf_cgroup_shm_header_t *header = mapped;
@@ -194,22 +193,17 @@ static inline void ebpf_cgroup_set_target_data(ebpf_cgroup_target_t *out, netdat
193 */
194 static ebpf_cgroup_target_t *ebpf_cgroup_find_or_create(netdata_ebpf_cgroup_shm_body_t *ptr)
195 {
197 - ebpf_cgroup_target_t *ect, *prev;
198 - for (ect = ebpf_cgroup_pids, prev = ebpf_cgroup_pids; ect; prev = ect, ect = ect->next) {
196 + for (ebpf_cgroup_target_t *ect = ebpf_cgroup_pids; ect; ect = ect->next) {
197 if (ect->hash == ptr->hash && !strcmp(ect->name, ptr->name)) {
198 ect->updated = 1;
199 return ect;
200 }
201 }
202
205 - ebpf_cgroup_target_t *new_ect = callocz(1, sizeof(ebpf_cgroup_target_t));
206 -
203 + ebpf_cgroup_target_t *new_ect = callocz(1, sizeof(*new_ect));
204 ebpf_cgroup_set_target_data(new_ect, ptr);
208 - if (!ebpf_cgroup_pids) {
209 - ebpf_cgroup_pids = new_ect;
210 - } else {
211 - prev->next = new_ect;
212 - }
205 + new_ect->next = ebpf_cgroup_pids;
206 + ebpf_cgroup_pids = new_ect;
207
208 return new_ect;
209 }
@@ -232,24 +226,38 @@ static void ebpf_update_pid_link_list(ebpf_cgroup_target_t *ect, char *path)
226 if (!ff)
227 return;
228
235 - size_t lines = procfile_lines(ff), l;
236 - for (l = 0; l < lines; l++) {
229 + for (size_t l = 0; l < procfile_lines(ff); l++) {
230 int pid = (int)str2l(procfile_lineword(ff, l, 0));
238 - if (pid) {
239 - struct pid_on_target2 *pt, *prev;
240 - for (pt = ect->pids, prev = ect->pids; pt; prev = pt, pt = pt->next) {
241 - if (pt->pid == pid)
242 - break;
231 + if (!pid)
232 + continue;
233 +
234 + int found = 0;
235 + for (struct pid_on_target2 *pt = ect->pids; pt; pt = pt->next) {
236 + if (pt->pid == pid) {
237 + pt->updated = 1;
238 + found = 1;
239 + break;
240 }
241 + }
242
245 - if (!pt) {
246 - struct pid_on_target2 *w = callocz(1, sizeof(struct pid_on_target2));
247 - w->pid = pid;
248 - if (!ect->pids)
249 - ect->pids = w;
250 - else
251 - prev->next = w;
252 - }
243 + if (!found) {
244 + struct pid_on_target2 *w = callocz(1, sizeof(*w));
245 + w->pid = pid;
246 + w->updated = 1;
247 + w->next = ect->pids;
248 + ect->pids = w;
249 + }
250 + }
251 +
252 + struct pid_on_target2 **pt = &ect->pids;
253 + while (*pt) {
254 + if (!(*pt)->updated) {
255 + struct pid_on_target2 *tmp = *pt;
256 + *pt = tmp->next;
257 + freez(tmp);
258 + } else {
259 + (*pt)->updated = 0;
260 + pt = &(*pt)->next;
261 }
262 }
263
@@ -349,15 +357,16 @@ void ebpf_create_charts_on_systemd(ebpf_systemd_args_t *chart)
357 ebpf_commit_label();
358 // Let us keep original string that can be used in another place. Chart creation does not happen frequently.
359 char *move = strdupz(chart->dimension);
352 - while (move) {
353 - char *next_dim = strchr(move, ',');
360 + char *ptr = move;
361 + while (ptr) {
362 + char *next_dim = strchr(ptr, ',');
363 if (next_dim) {
364 *next_dim = '\0';
365 next_dim++;
366 }
367
359 - fprintf(stdout, "DIMENSION %s '' %s 1 1\n", move, chart->algorithm);
360 - move = next_dim;
368 + fprintf(stdout, "DIMENSION %s '' %s 1 1\n", ptr, chart->algorithm);
369 + ptr = next_dim;
370 }
371 freez(move);
372 }
@@ -366,7 +375,7 @@ void ebpf_create_charts_on_systemd(ebpf_systemd_args_t *chart)
375 // Cgroup main thread
376
377 /**
369 - * Cgroup integratin
378 + * Cgroup integration
379 *
380 * Thread responsible to call functions responsible to sync data between plugins.
381 *
@@ -381,8 +390,13 @@ void ebpf_cgroup_integration(void *ptr __maybe_unused)
390 heartbeat_init(&hb, USEC_PER_SEC);
391 //Plugin will be killed when it receives a signal
392 while (!ebpf_plugin_stop()) {
393 + if (ebpf_plugin_stop())
394 + break;
395 +
396 heartbeat_next(&hb);
397
398 + if (ebpf_plugin_stop())
399 + break;
400 // We are using a small heartbeat time to wake up thread,
401 // but we should not update so frequently the shared memory data
402 if (++counter >= NETDATA_EBPF_CGROUP_UPDATE) {
src/collectors/ebpf.plugin/ebpf_dcstat.c
+145 -69
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_dcstat.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static char *dcstat_counter_dimension_name[NETDATA_DCSTAT_IDX_END] = {"ratio", "reference", "slow", "miss"};
8 static netdata_syscall_stat_t dcstat_counter_aggregated_data[NETDATA_DCSTAT_IDX_END];
@@ -190,7 +191,8 @@ static void ebpf_dc_set_hash_tables(struct dc_bpf *obj)
191 */
192 netdata_ebpf_program_loaded_t ebpf_dc_update_load(ebpf_module_t *em)
193 {
193 - if (!strcmp(
194 + if (dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].optional &&
195 + !strcmp(
196 dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].optional,
197 dc_optional_name[NETDATA_DC_TARGET_LOOKUP_FAST].function_to_attach))
198 return EBPF_LOAD_TRAMPOLINE;
@@ -260,6 +262,8 @@ static inline int ebpf_dc_load_and_attach(struct dc_bpf *obj, ebpf_module_t *em)
262 void dcstat_update_publish(netdata_publish_dcstat_t *out, uint64_t cache_access, uint64_t not_found)
263 {
264 NETDATA_DOUBLE successful_access = (NETDATA_DOUBLE)(((long long)cache_access) - ((long long)not_found));
265 + if (successful_access < 0)
266 + successful_access = 0;
267 NETDATA_DOUBLE ratio = (cache_access) ? successful_access / (NETDATA_DOUBLE)cache_access : 0;
268
269 out->ratio = (long long)(ratio * 100);
@@ -465,9 +469,24 @@ static void ebpf_obsolete_dc_global(ebpf_module_t *em)
469 *
470 * @param ptr thread data.
471 */
472 +void ebpf_dcstat_unload_bpf(ebpf_module_t *em)
473 +{
474 +#ifdef LIBBPF_MAJOR_VERSION
475 + if (dc_bpf_obj) {
476 + dc_bpf__destroy(dc_bpf_obj);
477 + dc_bpf_obj = NULL;
478 + }
479 +#endif
480 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
481 + ebpf_unload_legacy_code(em->objects, em->probe_links);
482 + em->objects = NULL;
483 + em->probe_links = NULL;
484 + }
485 +}
486 +
487 static void ebpf_dcstat_exit(void *pptr)
488 {
470 - pids_fd[NETDATA_EBPF_PIDS_DCSTAT_IDX] = -1;
489 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_DCSTAT_IDX, -1);
490 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
491 if (!em)
492 return;
@@ -479,7 +498,7 @@ static void ebpf_dcstat_exit(void *pptr)
498 if (ebpf_read_dcstat.thread)
499 nd_thread_signal_cancel(ebpf_read_dcstat.thread);
500
482 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
501 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
502 netdata_mutex_lock(&lock);
503 if (em->cgroup_charts) {
504 ebpf_obsolete_dc_cgroup_charts(em);
@@ -496,24 +515,11 @@ static void ebpf_dcstat_exit(void *pptr)
515 netdata_mutex_unlock(&lock);
516 }
517
499 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
500 -
501 -#ifdef LIBBPF_MAJOR_VERSION
502 - if (dc_bpf_obj) {
503 - dc_bpf__destroy(dc_bpf_obj);
504 - dc_bpf_obj = NULL;
505 - }
506 -#endif
507 -
508 - if (em->objects) {
509 - ebpf_unload_legacy_code(em->objects, em->probe_links);
510 - em->objects = NULL;
511 - em->probe_links = NULL;
512 - }
518 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
519 + em->functions.bpf_unload(em);
520
521 netdata_mutex_lock(&ebpf_exit_cleanup);
522 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
516 - ebpf_update_stats(&plugin_statistics, em);
523 netdata_mutex_unlock(&ebpf_exit_cleanup);
524 }
525
@@ -537,6 +543,9 @@ static void ebpf_dcstat_apps_accumulator(netdata_dcstat_pid_t *out, int maps_per
543 netdata_dcstat_pid_t *total = &out[0];
544 uint64_t ct = total->ct;
545 for (i = 1; i < end; i++) {
546 + if (ebpf_plugin_stop())
547 + break;
548 +
549 netdata_dcstat_pid_t *w = &out[i];
550 total->cache_access += w->cache_access;
551 total->file_system += w->file_system;
@@ -546,7 +555,7 @@ static void ebpf_dcstat_apps_accumulator(netdata_dcstat_pid_t *out, int maps_per
555 ct = w->ct;
556
557 if (!total->name[0] && w->name[0])
549 - strncpyz(total->name, w->name, sizeof(total->name) - 1);
558 + strncpyz(total->name, w->name, sizeof(total->name));
559 }
560 total->ct = ct;
561 }
@@ -568,6 +577,9 @@ static void ebpf_read_dc_apps_table(int maps_per_core)
577
578 uint32_t key = 0, next_key = 0;
579 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
580 + if (ebpf_plugin_stop())
581 + break;
582 +
583 if (bpf_map_lookup_elem(fd, &key, cv)) {
584 goto end_dc_loop;
585 }
@@ -584,7 +596,7 @@ static void ebpf_read_dc_apps_table(int maps_per_core)
596 publish->curr.file_system = cv[0].file_system;
597 publish->curr.cache_access = cv[0].cache_access;
598 } else {
587 - if (kill((pid_t)key, 0)) { // No PID found
599 + if (kill((pid_t)key, 0) == -1 && errno == ESRCH) {
600 if (netdata_ebpf_reset_shm_pointer_unsafe(fd, key, NETDATA_EBPF_PIDS_DCSTAT_IDX))
601 memset(publish, 0, sizeof(*publish));
602 }
@@ -609,6 +621,9 @@ void ebpf_dcstat_sum_pids(netdata_publish_dcstat_t *publish, struct ebpf_pid_on_
621 {
622 memset(&publish->curr, 0, sizeof(netdata_publish_dcstat_pid_t));
623 for (; root; root = root->next) {
624 + if (ebpf_plugin_stop())
625 + break;
626 +
627 uint32_t pid = root->pid;
628 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_get_shm_pointer_unsafe(pid, NETDATA_EBPF_PIDS_DCSTAT_IDX);
629 if (!local_pid)
@@ -624,12 +639,15 @@ void ebpf_dcstat_sum_pids(netdata_publish_dcstat_t *publish, struct ebpf_pid_on_
639 /**
640 * Resume apps data
641 */
627 -void ebpf_dc_resume_apps_data()
642 +void ebpf_dc_resume_apps_data(void)
643 {
644 struct ebpf_target *w;
645
646 netdata_mutex_lock(&collect_data_mutex);
647 for (w = apps_groups_root_target; w; w = w->next) {
648 + if (ebpf_plugin_stop())
649 + break;
650 +
651 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_DCSTAT_IDX))))
652 continue;
653
@@ -655,6 +673,9 @@ static void ebpf_update_dc_cgroup()
673 ebpf_cgroup_target_t *ect;
674 netdata_mutex_lock(&mutex_cgroup_shm);
675 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
676 + if (ebpf_plugin_stop())
677 + break;
678 +
679 struct pid_on_target2 *pids;
680 for (pids = ect->pids; pids; pids = pids->next) {
681 uint32_t pid = pids->pid;
@@ -695,29 +716,53 @@ void ebpf_read_dcstat_thread(void *ptr)
716
717 uint32_t lifetime = em->lifetime;
718 uint32_t running_time = 0;
698 - pids_fd[NETDATA_EBPF_PIDS_DCSTAT_IDX] = dcstat_maps[NETDATA_DCSTAT_PID_STATS].map_fd;
719 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_DCSTAT_IDX, dcstat_maps[NETDATA_DCSTAT_PID_STATS].map_fd);
720 heartbeat_t hb;
700 - heartbeat_init(&hb, update_every * USEC_PER_SEC);
721 + heartbeat_init(&hb, USEC_PER_SEC);
722 while (!ebpf_plugin_stop() && running_time < lifetime) {
723 + if (ebpf_plugin_stop())
724 + break;
725 +
726 (void)heartbeat_next(&hb);
703 - if (ebpf_plugin_stop() || ++counter != update_every)
727 + if (ebpf_plugin_stop())
728 + break;
729 +
730 + if (++counter != update_every)
731 continue;
732
706 - sem_wait(shm_mutex_ebpf_integration);
733 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
734 + if (errno != ECANCELED)
735 + netdata_log_error("DCSTAT: Failed to wait on semaphore.");
736 + break;
737 + }
738 +
739 ebpf_read_dc_apps_table(maps_per_core);
740 ebpf_dc_resume_apps_data();
741 + if (ebpf_plugin_stop()) {
742 + if (sem_post(shm_mutex_ebpf_integration))
743 + netdata_log_error("DCSTAT: Failed to post semaphore.");
744 + break;
745 + }
746 +
747 if (cgroups && shm_ebpf_cgroup.header)
748 ebpf_update_dc_cgroup();
749
712 - sem_post(shm_mutex_ebpf_integration);
750 + if (sem_post(shm_mutex_ebpf_integration)) {
751 + netdata_log_error("DCSTAT: Failed to post semaphore.");
752 + break;
753 + }
754
755 counter = 0;
756
757 + if (ebpf_plugin_stop()) {
758 + break;
759 + }
760 +
761 netdata_mutex_lock(&ebpf_exit_cleanup);
717 - if (running_time && !em->running_time)
718 - running_time = update_every;
719 - else
762 + if (running_time)
763 running_time += update_every;
764 + else
765 + running_time = update_every;
766
767 em->running_time = running_time;
768 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -737,6 +782,9 @@ void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr)
782 struct ebpf_target *w;
783 int update_every = em->update_every;
784 for (w = root; w; w = w->next) {
785 + if (ebpf_plugin_stop())
786 + break;
787 +
788 if (unlikely(!w->exposed))
789 continue;
790
@@ -821,10 +869,9 @@ void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr)
869 *
870 * Read the table with number of calls for all functions
871 *
824 - * @param stats vector used to read data from control table.
872 * @param maps_per_core do I need to read all cores?
873 */
827 -static void ebpf_dc_read_global_tables(netdata_idx_t *stats, int maps_per_core)
874 +static void ebpf_dc_read_global_tables(int maps_per_core)
875 {
876 ebpf_read_global_table_stats(
877 dcstat_hash_values,
@@ -833,14 +880,6 @@ static void ebpf_dc_read_global_tables(netdata_idx_t *stats, int maps_per_core)
880 maps_per_core,
881 NETDATA_KEY_DC_REFERENCE,
882 NETDATA_DIRECTORY_CACHE_END);
836 -
837 - ebpf_read_global_table_stats(
838 - stats,
839 - dcstat_values,
840 - dcstat_maps[NETDATA_DCSTAT_CTRL].map_fd,
841 - maps_per_core,
842 - NETDATA_CONTROLLER_PID_TABLE_ADD,
843 - NETDATA_CONTROLLER_END);
883 }
884
885 /**
@@ -855,6 +894,9 @@ void ebpf_dcache_send_apps_data(struct ebpf_target *root)
894
895 netdata_mutex_lock(&collect_data_mutex);
896 for (w = root; w; w = w->next) {
897 + if (ebpf_plugin_stop())
898 + break;
899 +
900 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_DCSTAT_IDX))))
901 continue;
902
@@ -910,23 +952,18 @@ static void dcstat_send_global(netdata_publish_dcstat_t *publish)
952 publish, dcstat_hash_values[NETDATA_KEY_DC_REFERENCE], dcstat_hash_values[NETDATA_KEY_DC_MISS]);
953
954 netdata_publish_syscall_t *ptr = dcstat_counter_publish_aggregated;
913 - netdata_idx_t value = dcstat_hash_values[NETDATA_KEY_DC_REFERENCE];
914 - if (value != ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall) {
915 - ptr[NETDATA_DCSTAT_IDX_REFERENCE].ncall = value - ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall;
916 - ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall = value;
917 -
918 - value = dcstat_hash_values[NETDATA_KEY_DC_SLOW];
919 - ptr[NETDATA_DCSTAT_IDX_SLOW].ncall = value - ptr[NETDATA_DCSTAT_IDX_SLOW].pcall;
920 - ptr[NETDATA_DCSTAT_IDX_SLOW].pcall = value;
921 -
922 - value = dcstat_hash_values[NETDATA_KEY_DC_MISS];
923 - ptr[NETDATA_DCSTAT_IDX_MISS].ncall = value - ptr[NETDATA_DCSTAT_IDX_MISS].pcall;
924 - ptr[NETDATA_DCSTAT_IDX_MISS].pcall = value;
925 - } else {
926 - ptr[NETDATA_DCSTAT_IDX_REFERENCE].ncall = 0;
927 - ptr[NETDATA_DCSTAT_IDX_SLOW].ncall = 0;
928 - ptr[NETDATA_DCSTAT_IDX_MISS].ncall = 0;
929 - }
955 +
956 + netdata_idx_t ref_value = dcstat_hash_values[NETDATA_KEY_DC_REFERENCE];
957 + ptr[NETDATA_DCSTAT_IDX_REFERENCE].ncall = ref_value - ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall;
958 + ptr[NETDATA_DCSTAT_IDX_REFERENCE].pcall = ref_value;
959 +
960 + netdata_idx_t slow_value = dcstat_hash_values[NETDATA_KEY_DC_SLOW];
961 + ptr[NETDATA_DCSTAT_IDX_SLOW].ncall = slow_value - ptr[NETDATA_DCSTAT_IDX_SLOW].pcall;
962 + ptr[NETDATA_DCSTAT_IDX_SLOW].pcall = slow_value;
963 +
964 + netdata_idx_t miss_value = dcstat_hash_values[NETDATA_KEY_DC_MISS];
965 + ptr[NETDATA_DCSTAT_IDX_MISS].ncall = miss_value - ptr[NETDATA_DCSTAT_IDX_MISS].pcall;
966 + ptr[NETDATA_DCSTAT_IDX_MISS].pcall = miss_value;
967
968 ebpf_one_dimension_write_charts(
969 NETDATA_FILESYSTEM_FAMILY, NETDATA_DC_HIT_CHART, ptr[NETDATA_DCSTAT_IDX_RATIO].dimension, publish->ratio);
@@ -1087,8 +1124,11 @@ static void ebpf_obsolete_specific_dc_charts(char *type, int update_every)
1124 */
1125 void ebpf_dc_sum_cgroup_pids(netdata_publish_dcstat_t *publish, struct pid_on_target2 *root)
1126 {
1090 - memset(&publish->curr, 0, sizeof(netdata_dcstat_pid_t));
1127 + memset(&publish->curr, 0, sizeof(netdata_publish_dcstat_pid_t));
1128 while (root) {
1129 + if (ebpf_plugin_stop())
1130 + break;
1131 +
1132 netdata_dcstat_pid_t *src = &root->dc;
1133
1134 publish->curr.cache_access += src->cache_access;
@@ -1104,10 +1144,13 @@ void ebpf_dc_sum_cgroup_pids(netdata_publish_dcstat_t *publish, struct pid_on_ta
1144 *
1145 * Do necessary math to plot charts.
1146 */
1107 -void ebpf_dc_calc_chart_values()
1147 +void ebpf_dc_calc_chart_values(void)
1148 {
1149 ebpf_cgroup_target_t *ect;
1150 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1151 + if (ebpf_plugin_stop())
1152 + break;
1153 +
1154 ebpf_dc_sum_cgroup_pids(&ect->publish_dc, ect->pids);
1155 uint64_t cache = ect->publish_dc.curr.cache_access;
1156 uint64_t not_found = ect->publish_dc.curr.not_found;
@@ -1185,12 +1228,18 @@ static void ebpf_create_systemd_dc_charts(int update_every)
1228 .suffix = NETDATA_DC_REQUEST_NOT_FOUND_CHART,
1229 .dimension = "files"};
1230
1188 - if (!data_dc_not_cache.update_every)
1189 - data_dc_hit_ratio.update_every = data_dc_not_cache.update_every = data_dc_not_found.update_every =
1190 - data_dc_references.update_every = update_every;
1231 + if (!data_dc_not_cache.update_every) {
1232 + data_dc_hit_ratio.update_every = update_every;
1233 + data_dc_not_cache.update_every = update_every;
1234 + data_dc_not_found.update_every = update_every;
1235 + data_dc_references.update_every = update_every;
1236 + }
1237
1238 ebpf_cgroup_target_t *w;
1239 for (w = ebpf_cgroup_pids; w; w = w->next) {
1240 + if (ebpf_plugin_stop())
1241 + break;
1242 +
1243 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_DC_CHART))
1244 continue;
1245
@@ -1217,6 +1266,9 @@ static void ebpf_send_systemd_dc_charts()
1266 ebpf_cgroup_target_t *ect;
1267 collected_number value;
1268 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1269 + if (ebpf_plugin_stop())
1270 + break;
1271 +
1272 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_DC_CHART))) {
1273 continue;
1274 }
@@ -1305,6 +1357,9 @@ void ebpf_dc_send_cgroup_data(int update_every)
1357 }
1358
1359 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1360 + if (ebpf_plugin_stop())
1361 + break;
1362 +
1363 if (ect->systemd)
1364 continue;
1365
@@ -1341,17 +1396,21 @@ static void dcstat_collector(ebpf_module_t *em)
1396 int maps_per_core = em->maps_per_core;
1397 uint32_t running_time = 0;
1398 uint32_t lifetime = em->lifetime;
1344 - netdata_idx_t *stats = em->hash_table_stats;
1345 - memset(stats, 0, sizeof(em->hash_table_stats));
1399 while (!ebpf_plugin_stop() && running_time < lifetime) {
1400 + if (ebpf_plugin_stop())
1401 + break;
1402 +
1403 heartbeat_next(&hb);
1404
1349 - if (ebpf_plugin_stop() || ++counter != update_every)
1405 + if (ebpf_plugin_stop())
1406 + break;
1407 +
1408 + if (++counter != update_every)
1409 continue;
1410
1411 counter = 0;
1412 netdata_apps_integration_flags_t apps = em->apps_charts;
1354 - ebpf_dc_read_global_tables(stats, maps_per_core);
1413 + ebpf_dc_read_global_tables(maps_per_core);
1414
1415 netdata_mutex_lock(&lock);
1416
@@ -1360,16 +1419,24 @@ static void dcstat_collector(ebpf_module_t *em)
1419 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
1420 ebpf_dcache_send_apps_data(apps_groups_root_target);
1421
1422 + if (ebpf_plugin_stop()) {
1423 + netdata_mutex_unlock(&lock);
1424 + break;
1425 + }
1426 +
1427 if (cgroups && shm_ebpf_cgroup.header)
1428 ebpf_dc_send_cgroup_data(update_every);
1429
1430 netdata_mutex_unlock(&lock);
1431
1432 + if (ebpf_plugin_stop())
1433 + break;
1434 +
1435 netdata_mutex_lock(&ebpf_exit_cleanup);
1369 - if (running_time && !em->running_time)
1370 - running_time = update_every;
1371 - else
1436 + if (running_time)
1437 running_time += update_every;
1438 + else
1439 + running_time = update_every;
1440
1441 em->running_time = running_time;
1442 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -1471,8 +1538,13 @@ static int ebpf_dcstat_load_bpf(ebpf_module_t *em)
1538 dc_bpf_obj = dc_bpf__open();
1539 if (!dc_bpf_obj)
1540 ret = -1;
1474 - else
1541 + else {
1542 ret = ebpf_dc_load_and_attach(dc_bpf_obj, em);
1543 + if (ret) {
1544 + dc_bpf__destroy(dc_bpf_obj);
1545 + dc_bpf_obj = NULL;
1546 + }
1547 + }
1548 }
1549 #endif
1550
@@ -1496,6 +1568,10 @@ void ebpf_dcstat_thread(void *ptr)
1568 ebpf_module_t *em = (ebpf_module_t *)ptr;
1569 CLEANUP_FUNCTION_REGISTER(ebpf_dcstat_exit) cleanup_ptr = em;
1570
1571 + if (!ebpf_module_thread_has_valid_state(em)) {
1572 + goto enddcstat;
1573 + }
1574 +
1575 em->maps = dcstat_maps;
1576
1577 ebpf_update_pid_table(&dcstat_maps[NETDATA_DCSTAT_PID_STATS], em);
src/collectors/ebpf.plugin/ebpf_dcstat.h
+3 -3
@@ -3,7 +3,8 @@
3 #ifndef NETDATA_EBPF_DCSTAT_H
4 #define NETDATA_EBPF_DCSTAT_H 1
5
6 -#include "ebpf.h"
6 +#include "libbpf_api/ebpf.h"
7 +#include "collectors/collectors-ipc/ebpf-ipc.h"
8
9 // Module name & description
10 #define NETDATA_EBPF_MODULE_NAME_DCSTAT "dcstat"
@@ -38,7 +39,7 @@
39 // ARAL name
40 #define NETDATA_EBPF_DCSTAT_ARAL_NAME "ebpf_dcstat"
41
41 -// Unity
42 +// Unit
43 #define EBPF_COMMON_UNITS_FILES "files"
44
45 enum directory_cache_indexes {
@@ -67,7 +68,6 @@ enum directory_cache_targets { NETDATA_DC_TARGET_LOOKUP_FAST, NETDATA_DC_TARGET_
68
69 void ebpf_dcstat_thread(void *ptr);
70 void ebpf_dcstat_create_apps_charts(struct ebpf_module *em, void *ptr);
70 -void ebpf_dcstat_release(netdata_publish_dcstat_t *stat);
71 extern struct config dcstat_config;
72 extern netdata_ebpf_targets_t dc_targets[];
73 extern ebpf_local_maps_t dcstat_maps[];
src/collectors/ebpf.plugin/ebpf_disk.c
+203 -217
@@ -2,9 +2,11 @@
2
3 #include <sys/resource.h>
4 #include <stdlib.h>
5 +#include <unistd.h>
6
7 #include "ebpf.h"
8 #include "ebpf_disk.h"
9 +#include "libbpf_api/ebpf_library.h"
10
11 struct config disk_config = APPCONFIG_INITIALIZER;
12
@@ -39,12 +41,13 @@ static ebpf_local_maps_t disk_maps[] = {
41 static avl_tree_lock disk_tree;
42 netdata_ebpf_disks_t *disk_list = NULL;
43
42 -char *tracepoint_block_type = {"block"};
43 -char *tracepoint_block_issue = {"block_rq_issue"};
44 -char *tracepoint_block_rq_complete = {"block_rq_complete"};
44 +const char *tracepoint_block_type = "block";
45 +const char *tracepoint_block_issue = "block_rq_issue";
46 +const char *tracepoint_block_rq_complete = "block_rq_complete";
47
48 static int was_block_issue_enabled = 0;
49 static int was_block_rq_complete_enabled = 0;
50 +static bool disk_safe_clean = false;
51
52 static char **dimensions = NULL;
53 static netdata_syscall_stat_t disk_aggregated_data[NETDATA_EBPF_HIST_MAX_BINS];
@@ -52,9 +55,10 @@ static netdata_publish_syscall_t disk_publish_aggregated[NETDATA_EBPF_HIST_MAX_B
55
56 static netdata_idx_t *disk_hash_values = NULL;
57
55 -ebpf_publish_disk_t *plot_disks = NULL;
58 netdata_mutex_t plot_mutex;
59
60 +static netdata_mutex_t tracepoint_mutex;
61 +
62 #ifdef LIBBPF_MAJOR_VERSION
63 /**
64 * Set hash table
@@ -94,6 +98,30 @@ static inline int ebpf_disk_load_and_attach(struct disk_bpf *obj)
98 *
99 *****************************************************************/
100
101 +/**
102 + * Read file to string
103 + *
104 + * @param filename file to read
105 + * @param buffer buffer to store content
106 + * @param size buffer size
107 + *
108 + * @return It returns content length on success and -1 otherwise
109 + */
110 +static inline ssize_t ebpf_read_file_to_str(const char *filename, char *buffer, size_t size)
111 +{
112 + int fd = open(filename, O_RDONLY, 0);
113 + if (fd < 0)
114 + return -1;
115 +
116 + ssize_t file_length = read(fd, buffer, size - 1);
117 + close(fd);
118 + if (file_length <= 0)
119 + return -1;
120 +
121 + buffer[file_length] = '\0';
122 + return file_length;
123 +}
124 +
125 /**
126 * Parse start
127 *
@@ -107,20 +135,11 @@ static inline int ebpf_disk_load_and_attach(struct disk_bpf *obj)
135 static inline int ebpf_disk_parse_start(netdata_ebpf_disks_t *w, char *filename)
136 {
137 char content[FILENAME_MAX + 1];
110 - int fd = open(filename, O_RDONLY, 0);
111 - if (fd < 0) {
138 + ssize_t file_length = ebpf_read_file_to_str(filename, content, FILENAME_MAX);
139 + if (file_length <= 0)
140 return -1;
113 - }
114 -
115 - ssize_t file_length = read(fd, content, 4095);
116 - if (file_length > 0) {
117 - if (file_length > FILENAME_MAX)
118 - file_length = FILENAME_MAX;
141
120 - content[file_length] = '\0';
121 - w->start = strtoul(content, NULL, 10);
122 - }
123 - close(fd);
142 + w->start = strtoul(content, NULL, 10);
143
144 return 0;
145 }
@@ -137,29 +156,8 @@ static inline int ebpf_disk_parse_start(netdata_ebpf_disks_t *w, char *filename)
156 */
157 static inline int ebpf_parse_uevent(netdata_ebpf_disks_t *w, char *filename)
158 {
140 - char content[FILENAME_MAX + 1];
141 - int fd = open(filename, O_RDONLY, 0);
142 - if (fd < 0) {
143 - return -1;
144 - }
145 -
146 - ssize_t file_length = read(fd, content, FILENAME_MAX);
147 - if (file_length > 0) {
148 - if (file_length > FILENAME_MAX)
149 - file_length = FILENAME_MAX;
150 -
151 - content[file_length] = '\0';
152 -
153 - char *s = strstr(content, "PARTNAME=EFI");
154 - if (s) {
155 - w->main->boot_partition = w;
156 - w->flags |= NETDATA_DISK_HAS_EFI;
157 - w->boot_chart = strdupz("disk_bootsector");
158 - }
159 - }
160 - close(fd);
161 -
162 - return 0;
159 + (void)w;
160 + return access(filename, F_OK);
161 }
162
163 /**
@@ -173,20 +171,11 @@ static inline int ebpf_parse_uevent(netdata_ebpf_disks_t *w, char *filename)
171 static inline int ebpf_parse_size(netdata_ebpf_disks_t *w, char *filename)
172 {
173 char content[FILENAME_MAX + 1];
176 - int fd = open(filename, O_RDONLY, 0);
177 - if (fd < 0) {
174 + ssize_t file_length = ebpf_read_file_to_str(filename, content, FILENAME_MAX);
175 + if (file_length <= 0)
176 return -1;
179 - }
177
181 - ssize_t file_length = read(fd, content, FILENAME_MAX);
182 - if (file_length > 0) {
183 - if (file_length > FILENAME_MAX)
184 - file_length = FILENAME_MAX;
185 -
186 - content[file_length] = '\0';
187 - w->end = w->start + strtoul(content, NULL, 10) - 1;
188 - }
189 - close(fd);
178 + w->end = w->start + strtoul(content, NULL, 10) - 1;
179
180 return 0;
181 }
@@ -196,14 +185,15 @@ static inline int ebpf_parse_size(netdata_ebpf_disks_t *w, char *filename)
185 *
186 * Read disk information from /sys/block
187 *
199 - * @param w structure where data is stored
200 - * @param name disk name
188 + * @param w structure where data is stored
189 + * @param name disk name
190 + * @param main_disk pointer to main disk structure
191 + * @param bootsector_key bootsector key for the disk
192 */
202 -static void ebpf_read_disk_info(netdata_ebpf_disks_t *w, char *name)
193 +static void
194 +ebpf_read_disk_info(netdata_ebpf_disks_t *w, char *name, netdata_ebpf_disks_t **main_disk, uint32_t *bootsector_key)
195 {
204 - static netdata_ebpf_disks_t *main_disk = NULL;
205 - static uint32_t key = 0;
206 - char *path = {"/sys/block"};
196 + char *path = "/sys/block";
197 char disk[NETDATA_DISK_NAME_LEN + 1];
198 char filename[FILENAME_MAX + 1];
199 snprintfz(disk, NETDATA_DISK_NAME_LEN, "%s", name);
@@ -213,20 +203,22 @@ static void ebpf_read_disk_info(netdata_ebpf_disks_t *w, char *name)
203 }
204
205 length--;
216 - size_t curr = length;
217 - while (isdigit((int)disk[length])) {
206 + int has_digits = 0;
207 + while (length != (size_t)-1 && isdigit((int)disk[length])) {
208 disk[length--] = '\0';
209 + has_digits = 1;
210 }
211
221 - // We are looking for partition information, if it is a device we will ignore it.
222 - if (curr == length) {
223 - main_disk = w;
224 - key = MKDEV(w->major, w->minor);
225 - w->bootsector_key = key;
212 + // We are looking for partition information, if it is a device we will set it as main disk
213 + if (!has_digits) {
214 + *main_disk = w;
215 + *bootsector_key = MKDEV(w->major, w->minor);
216 + w->bootsector_key = *bootsector_key;
217 return;
218 }
228 - w->bootsector_key = key;
229 - w->main = main_disk;
219 +
220 + // This is a partition, link it to main disk
221 + w->bootsector_key = *bootsector_key;
222
223 snprintfz(filename, FILENAME_MAX, "%s/%s/%s/uevent", path, disk, name);
224 if (ebpf_parse_uevent(w, filename))
@@ -270,12 +262,7 @@ static int ebpf_compare_disks(void *a, void *b)
262 netdata_ebpf_disks_t *ptr1 = a;
263 netdata_ebpf_disks_t *ptr2 = b;
264
273 - if (ptr1->dev > ptr2->dev)
274 - return 1;
275 - if (ptr1->dev < ptr2->dev)
276 - return -1;
277 -
278 - return 0;
265 + return (ptr1->dev > ptr2->dev) - (ptr1->dev < ptr2->dev);
266 }
267
268 /**
@@ -290,57 +277,41 @@ static int ebpf_compare_disks(void *a, void *b)
277 */
278 static void update_disk_table(char *name, int major, int minor, time_t current_time)
279 {
280 + static netdata_ebpf_disks_t *main_disk = NULL;
281 + static uint32_t bootsector_key = 0;
282 +
283 netdata_ebpf_disks_t find;
294 - netdata_ebpf_disks_t *w;
295 - size_t length;
284 + size_t length = strlen(name);
285 + if (length >= NETDATA_DISK_NAME_LEN)
286 + length = NETDATA_DISK_NAME_LEN;
287
288 uint32_t dev = netdata_new_encode_dev(major, minor);
289 find.dev = dev;
290 netdata_ebpf_disks_t *ret = (netdata_ebpf_disks_t *)avl_search_lock(&disk_tree, (avl_t *)&find);
300 - if (ret) { // Disk is already present
291 + if (ret) {
292 ret->flags |= NETDATA_DISK_IS_HERE;
293 ret->last_update = current_time;
294 return;
295 }
296
306 - netdata_ebpf_disks_t *update_next = disk_list;
297 + netdata_ebpf_disks_t *w;
298 if (likely(disk_list)) {
308 - netdata_ebpf_disks_t *move = disk_list;
309 - while (move) {
310 - if (dev == move->dev)
311 - return;
312 -
313 - update_next = move;
314 - move = move->next;
315 - }
316 -
299 w = callocz(1, sizeof(netdata_ebpf_disks_t));
318 - length = strlen(name);
319 - if (length >= NETDATA_DISK_NAME_LEN)
320 - length = NETDATA_DISK_NAME_LEN;
321 -
322 - memcpy(w->family, name, length);
323 - w->family[length] = '\0';
324 - w->major = major;
325 - w->minor = minor;
326 - w->dev = netdata_new_encode_dev(major, minor);
300 + netdata_ebpf_disks_t *update_next = disk_list;
301 + while (update_next->next)
302 + update_next = update_next->next;
303 update_next->next = w;
304 } else {
329 - disk_list = callocz(1, sizeof(netdata_ebpf_disks_t));
330 - length = strlen(name);
331 - if (length >= NETDATA_DISK_NAME_LEN)
332 - length = NETDATA_DISK_NAME_LEN;
333 -
334 - memcpy(disk_list->family, name, length);
335 - disk_list->family[length] = '\0';
336 - disk_list->major = major;
337 - disk_list->minor = minor;
338 - disk_list->dev = netdata_new_encode_dev(major, minor);
339 -
340 - w = disk_list;
305 + disk_list = w = callocz(1, sizeof(netdata_ebpf_disks_t));
306 }
307
343 - ebpf_read_disk_info(w, name);
308 + memcpy(w->family, name, length);
309 + w->family[length] = '\0';
310 + w->major = major;
311 + w->minor = minor;
312 + w->dev = dev;
313 +
314 + ebpf_read_disk_info(w, name, &main_disk, &bootsector_key);
315
316 netdata_ebpf_disks_t *check;
317 check = (netdata_ebpf_disks_t *)avl_insert_lock(&disk_tree, (avl_t *)w);
@@ -362,7 +333,7 @@ static void update_disk_table(char *name, int major, int minor, time_t current_t
333 *
334 * @return It returns 0 on success and -1 otherwise
335 */
365 -static int read_local_disks()
336 +static int read_local_disks(void)
337 {
338 char filename[FILENAME_MAX + 1];
339 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, NETDATA_EBPF_PROC_PARTITIONS);
@@ -424,49 +395,44 @@ void ebpf_update_disks(ebpf_module_t *em)
395 *
396 * Disable tracepoints when the plugin was responsible to enable it.
397 */
427 -static void ebpf_disk_disable_tracepoints()
398 +static void ebpf_disk_disable_tracepoints(void)
399 {
429 - char *default_message = {"Cannot disable the tracepoint"};
430 - if (!was_block_issue_enabled) {
400 + const char *default_message = "Cannot disable the tracepoint";
401 + int block_issue_enabled;
402 + int block_rq_complete_enabled;
403 +
404 + netdata_mutex_lock(&tracepoint_mutex);
405 + block_issue_enabled = was_block_issue_enabled;
406 + block_rq_complete_enabled = was_block_rq_complete_enabled;
407 + netdata_mutex_unlock(&tracepoint_mutex);
408 +
409 + if (!block_issue_enabled) {
410 if (ebpf_disable_tracing_values(tracepoint_block_type, tracepoint_block_issue))
411 netdata_log_error("%s %s/%s.", default_message, tracepoint_block_type, tracepoint_block_issue);
412 }
413
435 - if (!was_block_rq_complete_enabled) {
414 + if (!block_rq_complete_enabled) {
415 if (ebpf_disable_tracing_values(tracepoint_block_type, tracepoint_block_rq_complete))
416 netdata_log_error("%s %s/%s.", default_message, tracepoint_block_type, tracepoint_block_rq_complete);
417 }
418 }
419
441 -/**
442 - * Cleanup plot disks
443 - *
444 - * Clean disk list
445 - */
446 -static void ebpf_cleanup_plot_disks()
447 -{
448 - ebpf_publish_disk_t *move = plot_disks, *next;
449 - while (move) {
450 - next = move->next;
451 -
452 - freez(move);
453 -
454 - move = next;
455 - }
456 - plot_disks = NULL;
457 -}
458 -
420 /**
421 * Cleanup Disk List
422 */
462 -static void ebpf_cleanup_disk_list()
423 +static void ebpf_cleanup_disk_list(void)
424 {
425 netdata_ebpf_disks_t *move = disk_list;
426 while (move) {
427 netdata_ebpf_disks_t *next = move->next;
428
429 freez(move->histogram.name);
469 - freez(move->boot_chart);
430 + move->histogram.name = NULL;
431 + freez(move->histogram.title);
432 + move->histogram.title = NULL;
433 + freez(move->histogram.ctx);
434 + move->histogram.ctx = NULL;
435 +
436 freez(move);
437
438 move = next;
@@ -483,21 +449,20 @@ static void ebpf_cleanup_disk_list()
449 */
450 static void ebpf_obsolete_disk_global(ebpf_module_t *em)
451 {
486 - ebpf_publish_disk_t *move = plot_disks;
452 + netdata_ebpf_disks_t *move = disk_list;
453 while (move) {
488 - netdata_ebpf_disks_t *ned = move->plot;
489 - uint32_t flags = ned->flags;
454 + uint32_t flags = move->flags;
455 if (flags & NETDATA_DISK_CHART_CREATED) {
456 ebpf_write_chart_obsolete(
492 - ned->histogram.name,
493 - ned->family,
457 + move->histogram.name,
458 + move->family,
459 "",
460 "Disk latency",
461 EBPF_COMMON_UNITS_CALLS_PER_SEC,
497 - ned->family,
462 + move->family,
463 NETDATA_EBPF_CHART_TYPE_STACKED,
464 NETDATA_EBPF_DISK_LATENCY_CONTEXT,
500 - ned->histogram.order,
465 + move->histogram.order,
466 em->update_every);
467 }
468
@@ -505,50 +470,47 @@ static void ebpf_obsolete_disk_global(ebpf_module_t *em)
470 }
471 }
472
508 -/**
509 - * Disk exit.
510 - *
511 - * Cancel child and exit.
512 - *
513 - * @param ptr thread data.
514 - */
473 static void ebpf_disk_exit(void *pptr)
474 {
475 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
476 if (!em)
477 return;
478
521 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
522 - netdata_mutex_lock(&lock);
479 + if (!disk_safe_clean) {
480 + netdata_mutex_lock(&ebpf_exit_cleanup);
481 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
482 + netdata_mutex_unlock(&ebpf_exit_cleanup);
483 + return;
484 + }
485
486 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
487 + netdata_mutex_lock(&lock);
488 ebpf_obsolete_disk_global(em);
525 -
489 netdata_mutex_unlock(&lock);
490 fflush(stdout);
491 }
529 - ebpf_disk_disable_tracepoints();
492
531 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, disk_maps, EBPF_ACTION_STAT_REMOVE);
532 -
533 - if (em->objects) {
534 - ebpf_unload_legacy_code(em->objects, em->probe_links);
535 - em->objects = NULL;
536 - em->probe_links = NULL;
537 - }
493 + ebpf_disk_disable_tracepoints();
494
539 - if (dimensions)
495 + if (dimensions) {
496 ebpf_histogram_dimension_cleanup(dimensions, NETDATA_EBPF_HIST_MAX_BINS);
497 + dimensions = NULL;
498 + }
499
500 freez(disk_hash_values);
501 disk_hash_values = NULL;
502 +
503 netdata_mutex_destroy(&plot_mutex);
504 + netdata_mutex_destroy(&tracepoint_mutex);
505 +
506 + if (disk_list)
507 + ebpf_cleanup_disk_list();
508
546 - ebpf_cleanup_plot_disks();
547 - ebpf_cleanup_disk_list();
509 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
510 + em->functions.bpf_unload(em);
511
512 netdata_mutex_lock(&ebpf_exit_cleanup);
513 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
551 - ebpf_update_stats(&plugin_statistics, em);
514 netdata_mutex_unlock(&ebpf_exit_cleanup);
515 }
516
@@ -561,34 +523,15 @@ static void ebpf_disk_exit(void *pptr)
523 /**
524 * Fill Plot list
525 *
526 + * Mark disk as needing to be plotted
527 + *
528 * @param ptr a pointer for current disk
529 */
530 static void ebpf_fill_plot_disks(netdata_ebpf_disks_t *ptr)
531 {
532 netdata_mutex_lock(&plot_mutex);
569 - ebpf_publish_disk_t *w;
570 - if (likely(plot_disks)) {
571 - ebpf_publish_disk_t *move = plot_disks, *store = plot_disks;
572 - while (move) {
573 - if (move->plot == ptr) {
574 - netdata_mutex_unlock(&plot_mutex);
575 - return;
576 - }
577 -
578 - store = move;
579 - move = move->next;
580 - }
581 -
582 - w = callocz(1, sizeof(ebpf_publish_disk_t));
583 - w->plot = ptr;
584 - store->next = w;
585 - } else {
586 - plot_disks = callocz(1, sizeof(ebpf_publish_disk_t));
587 - plot_disks->plot = ptr;
588 - }
589 - netdata_mutex_unlock(&plot_mutex);
590 -
533 ptr->flags |= NETDATA_DISK_ADDED_TO_PLOT_LIST;
534 + netdata_mutex_unlock(&plot_mutex);
535 }
536
537 /**
@@ -608,6 +551,9 @@ static void read_hard_disk_tables(int table, int maps_per_core)
551 netdata_ebpf_disks_t *ret = NULL;
552
553 while (bpf_map_get_next_key(table, &key, &next_key) == 0) {
554 + if (ebpf_plugin_stop())
555 + break;
556 +
557 int test = bpf_map_lookup_elem(table, &key, values);
558 if (test < 0) {
559 key = next_key;
@@ -617,10 +563,7 @@ static void read_hard_disk_tables(int table, int maps_per_core)
563 netdata_ebpf_disks_t find;
564 find.dev = key.dev;
565
620 - if (likely(ret)) {
621 - if (find.dev != ret->dev)
622 - ret = (netdata_ebpf_disks_t *)avl_search_lock(&disk_tree, (avl_t *)&find);
623 - } else
566 + if (!ret || find.dev != ret->dev)
567 ret = (netdata_ebpf_disks_t *)avl_search_lock(&disk_tree, (avl_t *)&find);
568
569 // Disk was inserted after we parse /proc/partitions
@@ -720,33 +663,39 @@ static void ebpf_create_hd_charts(netdata_ebpf_disks_t *w, int update_every)
663 /**
664 * Remove pointer from plot
665 *
723 - * Remove pointer from plot list when the disk is not present.
666 + * Remove disk from tracking when not present - now iterates disk_list directly
667 */
668 static void ebpf_remove_pointer_from_plot_disk(ebpf_module_t *em)
669 {
670 time_t current_time = now_realtime_sec();
671 time_t limit = 10 * em->update_every;
672 netdata_mutex_lock(&plot_mutex);
730 - ebpf_publish_disk_t *move = plot_disks, *prev = plot_disks;
673 + netdata_ebpf_disks_t *move = disk_list, *prev = NULL;
674 int update_every = em->update_every;
675 while (move) {
733 - netdata_ebpf_disks_t *ned = move->plot;
734 - uint32_t flags = ned->flags;
676 + uint32_t flags = move->flags;
677
736 - if (!(flags & NETDATA_DISK_IS_HERE) && ((current_time - ned->last_update) > limit)) {
737 - ebpf_obsolete_hd_charts(ned, update_every);
738 - avl_t *ret = (avl_t *)avl_remove_lock(&disk_tree, (avl_t *)ned);
678 + if (!(flags & NETDATA_DISK_IS_HERE) && ((current_time - move->last_update) > limit)) {
679 + ebpf_obsolete_hd_charts(move, update_every);
680 + avl_t *ret = (avl_t *)avl_remove_lock(&disk_tree, (avl_t *)move);
681 UNUSED(ret);
740 - if (move == plot_disks) {
741 - freez(move);
742 - plot_disks = NULL;
743 - break;
744 - } else {
682 + if (prev) {
683 prev->next = move->next;
746 - ebpf_publish_disk_t *clean = move;
684 + netdata_ebpf_disks_t *clean = move;
685 move = move->next;
686 + freez(clean->histogram.name);
687 + freez(clean->histogram.title);
688 + freez(clean->histogram.ctx);
689 freez(clean);
690 continue;
691 + } else {
692 + disk_list = move->next;
693 + freez(move->histogram.name);
694 + freez(move->histogram.title);
695 + freez(move->histogram.ctx);
696 + freez(move);
697 + move = disk_list;
698 + continue;
699 }
700 }
701
@@ -766,25 +715,24 @@ static void ebpf_remove_pointer_from_plot_disk(ebpf_module_t *em)
715 static void ebpf_latency_send_hd_data(int update_every)
716 {
717 netdata_mutex_lock(&plot_mutex);
769 - if (!plot_disks) {
718 + if (!disk_list) {
719 netdata_mutex_unlock(&plot_mutex);
720 return;
721 }
722
774 - ebpf_publish_disk_t *move = plot_disks;
723 + netdata_ebpf_disks_t *move = disk_list;
724 while (move) {
776 - netdata_ebpf_disks_t *ned = move->plot;
777 - uint32_t flags = ned->flags;
725 + uint32_t flags = move->flags;
726 if (!(flags & NETDATA_DISK_CHART_CREATED)) {
779 - ebpf_create_hd_charts(ned, update_every);
727 + ebpf_create_hd_charts(move, update_every);
728 }
729
730 if ((flags & NETDATA_DISK_CHART_CREATED)) {
731 write_histogram_chart(
784 - ned->histogram.name, ned->family, ned->histogram.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
732 + move->histogram.name, move->family, move->histogram.histogram, dimensions, NETDATA_EBPF_HIST_MAX_BINS);
733 }
734
787 - ned->flags &= ~NETDATA_DISK_IS_HERE;
735 + move->flags &= ~NETDATA_DISK_IS_HERE;
736
737 move = move->next;
738 }
@@ -806,9 +754,15 @@ static void disk_collector(ebpf_module_t *em)
754 uint32_t running_time = 0;
755 uint32_t lifetime = em->lifetime;
756 while (!ebpf_plugin_stop() && running_time < lifetime) {
757 + if (ebpf_plugin_stop())
758 + break;
759 +
760 heartbeat_next(&hb);
761
811 - if (ebpf_plugin_stop() || ++counter != update_every)
762 + if (ebpf_plugin_stop())
763 + break;
764 +
765 + if (++counter != update_every)
766 continue;
767
768 counter = 0;
@@ -821,11 +775,14 @@ static void disk_collector(ebpf_module_t *em)
775
776 ebpf_update_disks(em);
777
778 + if (ebpf_plugin_stop())
779 + break;
780 +
781 netdata_mutex_lock(&ebpf_exit_cleanup);
825 - if (running_time && !em->running_time)
826 - running_time = update_every;
827 - else
782 + if (running_time)
783 running_time += update_every;
784 + else
785 + running_time = update_every;
786
787 em->running_time = running_time;
788 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -854,7 +811,10 @@ static int ebpf_disk_enable_tracepoints()
811 if (ebpf_enable_tracing_values(tracepoint_block_type, tracepoint_block_issue))
812 return -1;
813 }
814 +
815 + netdata_mutex_lock(&tracepoint_mutex);
816 was_block_issue_enabled = test;
817 + netdata_mutex_unlock(&tracepoint_mutex);
818
819 test = ebpf_is_tracepoint_enabled(tracepoint_block_type, tracepoint_block_rq_complete);
820 if (test == -1)
@@ -863,7 +823,10 @@ static int ebpf_disk_enable_tracepoints()
823 if (ebpf_enable_tracing_values(tracepoint_block_type, tracepoint_block_rq_complete))
824 return -1;
825 }
826 +
827 + netdata_mutex_lock(&tracepoint_mutex);
828 was_block_rq_complete_enabled = test;
829 + netdata_mutex_unlock(&tracepoint_mutex);
830
831 return 0;
832 }
@@ -893,8 +856,12 @@ static int ebpf_disk_load_bpf(ebpf_module_t *em)
856 ret = -1;
857 else {
858 ret = ebpf_disk_load_and_attach(disk_bpf_obj);
896 - if (!ret)
859 + if (ret) {
860 + disk_bpf__destroy(disk_bpf_obj);
861 + disk_bpf_obj = NULL;
862 + } else {
863 ebpf_disk_set_hash_table(disk_bpf_obj);
864 + }
865 }
866 }
867 #endif
@@ -920,19 +887,34 @@ void ebpf_disk_thread(void *ptr)
887
888 CLEANUP_FUNCTION_REGISTER(ebpf_disk_exit) cleanup_ptr = em;
889
890 + disk_safe_clean = false;
891 +
892 + if (!ebpf_module_thread_has_valid_state(em)) {
893 + goto enddisk;
894 + }
895 +
896 em->maps = disk_maps;
897
925 - if (ebpf_disk_enable_tracepoints()) {
898 + if (netdata_mutex_init(&plot_mutex)) {
899 + netdata_log_error("Cannot initialize local mutex");
900 goto enddisk;
901 }
902
929 - avl_init_lock(&disk_tree, ebpf_compare_disks);
930 - if (read_local_disks()) {
903 + if (netdata_mutex_init(&tracepoint_mutex)) {
904 + netdata_log_error("Cannot initialize tracepoint mutex");
905 goto enddisk;
906 }
907
934 - if (netdata_mutex_init(&plot_mutex)) {
935 - netdata_log_error("Cannot initialize local mutex");
908 + disk_safe_clean = true;
909 +
910 + if (ebpf_disk_enable_tracepoints()) {
911 + goto enddisk;
912 + }
913 +
914 + // disk_safe_clean already true - mutexes will be cleaned up on exit
915 +
916 + avl_init_lock(&disk_tree, ebpf_compare_disks);
917 + if (read_local_disks()) {
918 goto enddisk;
919 }
920
@@ -947,6 +929,10 @@ void ebpf_disk_thread(void *ptr)
929 int algorithms[NETDATA_EBPF_HIST_MAX_BINS];
930 ebpf_fill_algorithms(algorithms, NETDATA_EBPF_HIST_MAX_BINS, NETDATA_EBPF_INCREMENTAL_IDX);
931 dimensions = ebpf_fill_histogram_dimension(NETDATA_EBPF_HIST_MAX_BINS);
932 + if (!dimensions) {
933 + netdata_log_error("Cannot allocate histogram dimensions");
934 + goto enddisk;
935 + }
936
937 ebpf_global_labels(
938 disk_aggregated_data, disk_publish_aggregated, dimensions, dimensions, algorithms, NETDATA_EBPF_HIST_MAX_BINS);
src/collectors/ebpf.plugin/ebpf_disk.h
+33 -19
@@ -8,6 +8,7 @@
8 #define NETDATA_EBPF_DISK_MODULE_DESC "Monitor disk latency independent of filesystem."
9
10 #include "libnetdata/avl/avl.h"
11 +#include "libnetdata/libnetdata.h"
12 #include "libbpf_api/ebpf.h"
13
14 #define NETDATA_EBPF_PROC_PARTITIONS "/proc/partitions"
@@ -23,8 +24,7 @@ enum netdata_latency_disks_flags {
24 NETDATA_DISK_NONE = 0,
25 NETDATA_DISK_ADDED_TO_PLOT_LIST = 1,
26 NETDATA_DISK_CHART_CREATED = 2,
26 - NETDATA_DISK_IS_HERE = 4,
27 - NETDATA_DISK_HAS_EFI = 8
27 + NETDATA_DISK_IS_HERE = 4
28 };
29
30 /*
@@ -32,30 +32,49 @@ enum netdata_latency_disks_flags {
32 * I decided to bring it as internal definition, to avoid include linux/genhd.h.
33 */
34 #define NETDATA_DISK_NAME_LEN 32
35 +
36 typedef struct netdata_ebpf_disks {
36 - // Search
37 + uint64_t start;
38 + uint64_t end;
39 +
40 avl_t avl;
41 uint32_t dev;
42 uint32_t major;
43 uint32_t minor;
44 uint32_t bootsector_key;
42 - uint64_t start; // start sector
43 - uint64_t end; // end sector
44 -
45 - // Print information
46 - char family[NETDATA_DISK_NAME_LEN + 1];
47 - char *boot_chart;
48 -
49 - netdata_ebpf_histogram_t histogram;
50 -
45 uint32_t flags;
46 +
47 time_t last_update;
48
54 - struct netdata_ebpf_disks *main;
55 - struct netdata_ebpf_disks *boot_partition;
49 + char family[NETDATA_DISK_NAME_LEN + 1];
50 +
51 struct netdata_ebpf_disks *next;
52 +
53 + netdata_ebpf_histogram_t histogram;
54 } netdata_ebpf_disks_t;
55
56 +static inline void ebpf_disks_init(netdata_ebpf_disks_t *d)
57 +{
58 + d->start = 0;
59 + d->end = 0;
60 + d->dev = 0;
61 + d->major = 0;
62 + d->minor = 0;
63 + d->bootsector_key = 0;
64 + d->flags = 0;
65 + d->last_update = 0;
66 + d->family[0] = '\0';
67 + d->next = NULL;
68 + memset(&d->histogram, 0, sizeof(d->histogram));
69 +}
70 +
71 +static inline void ebpf_disks_cleanup(netdata_ebpf_disks_t *d)
72 +{
73 + freez(d->histogram.name);
74 + freez(d->histogram.title);
75 + freez(d->histogram.ctx);
76 +}
77 +
78 enum ebpf_disk_tables { NETDATA_DISK_IO };
79
80 typedef struct block_key {
@@ -63,11 +82,6 @@ typedef struct block_key {
82 uint32_t dev;
83 } block_key_t;
84
66 -typedef struct netdata_ebpf_publish_disk {
67 - netdata_ebpf_disks_t *plot;
68 - struct netdata_ebpf_publish_disk *next;
69 -} ebpf_publish_disk_t;
70 -
85 #define NETDATA_EBPF_DISK_LATENCY_CONTEXT "disk.latency_io"
86
87 extern struct config disk_config;
src/collectors/ebpf.plugin/ebpf_fd.c
+215 -74
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_fd.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static char *fd_dimension_names[NETDATA_FD_SYSCALL_END] = {"open", "close"};
8 static char *fd_id_names[NETDATA_FD_SYSCALL_END] = {"do_sys_open", "__close_fd"};
@@ -58,6 +59,9 @@ static netdata_idx_t fd_hash_values[NETDATA_FD_COUNTER];
59 static netdata_idx_t *fd_values = NULL;
60
61 netdata_fd_stat_t *fd_vector = NULL;
62 +static bool fd_safe_clean = false;
63 +
64 +static int fd_use_close_fd = -1;
65
66 netdata_ebpf_targets_t fd_targets[] = {
67 {.name = "open", .mode = EBPF_LOAD_TRAMPOLINE},
@@ -75,6 +79,18 @@ struct netdata_static_thread ebpf_read_fd = {
79 .start_routine = NULL};
80
81 #ifdef LIBBPF_MAJOR_VERSION
82 +/**
83 + * Check if using close_fd
84 + *
85 + * @return true if using close_fd, false if using __close_fd
86 + */
87 +static inline int ebpf_fd_using_close_fd(void)
88 +{
89 + if (fd_use_close_fd == -1)
90 + fd_use_close_fd = !strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD]);
91 + return fd_use_close_fd;
92 +}
93 +
94 /**
95 * Disable probe
96 *
@@ -86,7 +102,7 @@ static inline void ebpf_fd_disable_probes(struct fd_bpf *obj)
102 {
103 bpf_program__set_autoload(obj->progs.netdata_sys_open_kprobe, false);
104 bpf_program__set_autoload(obj->progs.netdata_sys_open_kretprobe, false);
89 - if (!strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD])) {
105 + if (ebpf_fd_using_close_fd()) {
106 bpf_program__set_autoload(obj->progs.netdata___close_fd_kretprobe, false);
107 bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false);
108 bpf_program__set_autoload(obj->progs.netdata_close_fd_kprobe, false);
@@ -106,7 +122,7 @@ static inline void ebpf_fd_disable_probes(struct fd_bpf *obj)
122 */
123 static inline void ebpf_disable_specific_probes(struct fd_bpf *obj)
124 {
109 - if (!strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD])) {
125 + if (ebpf_fd_using_close_fd()) {
126 bpf_program__set_autoload(obj->progs.netdata___close_fd_kretprobe, false);
127 bpf_program__set_autoload(obj->progs.netdata___close_fd_kprobe, false);
128 } else {
@@ -141,7 +157,7 @@ static inline void ebpf_disable_trampoline(struct fd_bpf *obj)
157 */
158 static inline void ebpf_disable_specific_trampoline(struct fd_bpf *obj)
159 {
144 - if (!strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD])) {
160 + if (ebpf_fd_using_close_fd()) {
161 bpf_program__set_autoload(obj->progs.netdata___close_fd_fentry, false);
162 bpf_program__set_autoload(obj->progs.netdata___close_fd_fexit, false);
163 } else {
@@ -162,7 +178,7 @@ static void ebpf_set_trampoline_target(struct fd_bpf *obj)
178 bpf_program__set_attach_target(obj->progs.netdata_sys_open_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_OPEN].name);
179 bpf_program__set_attach_target(obj->progs.netdata_sys_open_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_OPEN].name);
180
165 - if (!strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD])) {
181 + if (ebpf_fd_using_close_fd()) {
182 bpf_program__set_attach_target(
183 obj->progs.netdata_close_fd_fentry, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
184 bpf_program__set_attach_target(obj->progs.netdata_close_fd_fexit, 0, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
@@ -197,7 +213,7 @@ static int ebpf_fd_attach_probe(struct fd_bpf *obj)
213 if (ret)
214 return -1;
215
200 - if (!strcmp(fd_targets[NETDATA_FD_SYSCALL_CLOSE].name, close_targets[NETDATA_FD_CLOSE_FD])) {
216 + if (ebpf_fd_using_close_fd()) {
217 obj->links.netdata_close_fd_kretprobe = bpf_program__attach_kprobe(
218 obj->progs.netdata_close_fd_kretprobe, true, fd_targets[NETDATA_FD_SYSCALL_CLOSE].name);
219 ret = libbpf_get_error(obj->links.netdata_close_fd_kretprobe);
@@ -249,7 +265,7 @@ static inline void ebpf_fd_fill_address(ebpf_addresses_t *address, char **target
265 *
266 * @return It returns 0 on success and -1 otherwise.
267 */
252 -static int ebpf_fd_set_target_values()
268 +static int ebpf_fd_set_target_values(void)
269 {
270 ebpf_addresses_t address = {.function = NULL, .hash = 0, .addr = 0};
271 ebpf_fd_fill_address(&address, close_targets);
@@ -277,11 +293,32 @@ static int ebpf_fd_set_target_values()
293 *
294 * @param obj is the main structure for bpf objects.
295 */
280 -static void ebpf_fd_set_hash_tables(struct fd_bpf *obj)
296 +static int ebpf_fd_set_hash_tables(struct fd_bpf *obj)
297 {
282 - fd_maps[NETDATA_FD_GLOBAL_STATS].map_fd = bpf_map__fd(obj->maps.tbl_fd_global);
283 - fd_maps[NETDATA_FD_PID_STATS].map_fd = bpf_map__fd(obj->maps.tbl_fd_pid);
284 - fd_maps[NETDATA_FD_CONTROLLER].map_fd = bpf_map__fd(obj->maps.fd_ctrl);
298 + int map_fd;
299 +
300 + map_fd = bpf_map__fd(obj->maps.tbl_fd_global);
301 + if (map_fd < 0) {
302 + netdata_log_error("Failed to get fd for tbl_fd_global map");
303 + return -1;
304 + }
305 + fd_maps[NETDATA_FD_GLOBAL_STATS].map_fd = map_fd;
306 +
307 + map_fd = bpf_map__fd(obj->maps.tbl_fd_pid);
308 + if (map_fd < 0) {
309 + netdata_log_error("Failed to get fd for tbl_fd_pid map");
310 + return -1;
311 + }
312 + fd_maps[NETDATA_FD_PID_STATS].map_fd = map_fd;
313 +
314 + map_fd = bpf_map__fd(obj->maps.fd_ctrl);
315 + if (map_fd < 0) {
316 + netdata_log_error("Failed to get fd for fd_ctrl map");
317 + return -1;
318 + }
319 + fd_maps[NETDATA_FD_CONTROLLER].map_fd = map_fd;
320 +
321 + return 0;
322 }
323
324 /**
@@ -340,9 +377,11 @@ static inline int ebpf_fd_load_and_attach(struct fd_bpf *obj, ebpf_module_t *em)
377
378 ret = (test == EBPF_LOAD_TRAMPOLINE) ? fd_bpf__attach(obj) : ebpf_fd_attach_probe(obj);
379 if (!ret) {
343 - ebpf_fd_set_hash_tables(obj);
380 + ret = ebpf_fd_set_hash_tables(obj);
381
345 - ebpf_update_controller(fd_maps[NETDATA_FD_CONTROLLER].map_fd, em);
382 + if (!ret) {
383 + ebpf_update_controller(fd_maps[NETDATA_FD_CONTROLLER].map_fd, em);
384 + }
385 }
386
387 return ret;
@@ -375,7 +414,7 @@ static void ebpf_obsolete_fd_services(ebpf_module_t *em, char *id)
414 NETDATA_APPS_FILE_GROUP,
415 NETDATA_EBPF_CHART_TYPE_STACKED,
416 NETDATA_CGROUP_FD_OPEN_CONTEXT,
378 - 20270,
417 + NETDATA_EBPF_FD_CHART_PRIORITY_OPEN,
418 em->update_every);
419
420 if (em->mode < MODE_ENTRY) {
@@ -388,7 +427,7 @@ static void ebpf_obsolete_fd_services(ebpf_module_t *em, char *id)
427 NETDATA_APPS_FILE_GROUP,
428 NETDATA_EBPF_CHART_TYPE_STACKED,
429 NETDATA_CGROUP_FD_OPEN_ERR_CONTEXT,
391 - 20271,
430 + NETDATA_EBPF_FD_CHART_PRIORITY_OPEN_ERR,
431 em->update_every);
432 }
433
@@ -401,7 +440,7 @@ static void ebpf_obsolete_fd_services(ebpf_module_t *em, char *id)
440 NETDATA_APPS_FILE_GROUP,
441 NETDATA_EBPF_CHART_TYPE_STACKED,
442 NETDATA_CGROUP_FD_CLOSE_CONTEXT,
404 - 20272,
443 + NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE,
444 em->update_every);
445
446 if (em->mode < MODE_ENTRY) {
@@ -414,7 +453,7 @@ static void ebpf_obsolete_fd_services(ebpf_module_t *em, char *id)
453 NETDATA_APPS_FILE_GROUP,
454 NETDATA_EBPF_CHART_TYPE_STACKED,
455 NETDATA_CGROUP_FD_CLOSE_ERR_CONTEXT,
417 - 20273,
456 + NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE_ERR,
457 em->update_every);
458 }
459 }
@@ -468,7 +507,7 @@ void ebpf_obsolete_fd_apps_charts(struct ebpf_module *em)
507 NETDATA_APPS_FILE_FDS,
508 NETDATA_EBPF_CHART_TYPE_STACKED,
509 "app.ebpf_file_open",
471 - 20220,
510 + NETDATA_EBPF_FD_CHART_PRIORITY_APPS_OPEN,
511 update_every);
512
513 if (em->mode < MODE_ENTRY) {
@@ -481,7 +520,7 @@ void ebpf_obsolete_fd_apps_charts(struct ebpf_module *em)
520 NETDATA_APPS_FILE_FDS,
521 NETDATA_EBPF_CHART_TYPE_STACKED,
522 "app.ebpf_file_open_error",
484 - 20221,
523 + NETDATA_EBPF_FD_CHART_PRIORITY_APPS_OPEN_ERR,
524 update_every);
525 }
526
@@ -494,7 +533,7 @@ void ebpf_obsolete_fd_apps_charts(struct ebpf_module *em)
533 NETDATA_APPS_FILE_FDS,
534 NETDATA_EBPF_CHART_TYPE_STACKED,
535 "app.ebpf_file_closed",
497 - 20222,
536 + NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE,
537 update_every);
538
539 if (em->mode < MODE_ENTRY) {
@@ -507,7 +546,7 @@ void ebpf_obsolete_fd_apps_charts(struct ebpf_module *em)
546 NETDATA_APPS_FILE_FDS,
547 NETDATA_EBPF_CHART_TYPE_STACKED,
548 "app.ebpf_fd_close_error",
510 - 20223,
549 + NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE_ERR,
550 update_every);
551 }
552 w->charts_created &= ~(1 << EBPF_MODULE_FD_IDX);
@@ -558,21 +597,45 @@ static void ebpf_obsolete_fd_global(ebpf_module_t *em)
597 *
598 * @param ptr thread data.
599 */
600 +void ebpf_fd_unload_bpf(ebpf_module_t *em)
601 +{
602 +#ifdef LIBBPF_MAJOR_VERSION
603 + if (fd_bpf_obj) {
604 + fd_bpf__destroy(fd_bpf_obj);
605 + fd_bpf_obj = NULL;
606 + }
607 +#endif
608 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
609 + ebpf_unload_legacy_code(em->objects, em->probe_links);
610 + em->objects = NULL;
611 + em->probe_links = NULL;
612 + }
613 +}
614 +
615 static void ebpf_fd_exit(void *pptr)
616 {
563 - pids_fd[NETDATA_EBPF_PIDS_FD_IDX] = -1;
617 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_FD_IDX, -1);
618 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
619 if (!em)
620 return;
621
622 + if (!fd_safe_clean) {
623 + netdata_mutex_lock(&ebpf_exit_cleanup);
624 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
625 + netdata_mutex_unlock(&ebpf_exit_cleanup);
626 + return;
627 + }
628 +
629 netdata_mutex_lock(&lock);
630 collect_pids &= ~(1 << EBPF_MODULE_FD_IDX);
631 netdata_mutex_unlock(&lock);
632
572 - if (ebpf_read_fd.thread)
633 + if (ebpf_read_fd.thread) {
634 nd_thread_signal_cancel(ebpf_read_fd.thread);
635 + nd_thread_join(ebpf_read_fd.thread);
636 + }
637
575 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
638 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
639 netdata_mutex_lock(&lock);
640 if (em->cgroup_charts) {
641 ebpf_obsolete_fd_cgroup_charts(em);
@@ -589,23 +652,16 @@ static void ebpf_fd_exit(void *pptr)
652 netdata_mutex_unlock(&lock);
653 }
654
592 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
655 + freez(fd_vector);
656 + fd_vector = NULL;
657 + freez(fd_values);
658 + fd_values = NULL;
659
594 -#ifdef LIBBPF_MAJOR_VERSION
595 - if (fd_bpf_obj) {
596 - fd_bpf__destroy(fd_bpf_obj);
597 - fd_bpf_obj = NULL;
598 - }
599 -#endif
600 - if (em->objects) {
601 - ebpf_unload_legacy_code(em->objects, em->probe_links);
602 - em->objects = NULL;
603 - em->probe_links = NULL;
604 - }
660 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
661 + em->functions.bpf_unload(em);
662
663 netdata_mutex_lock(&ebpf_exit_cleanup);
664 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
608 - ebpf_update_stats(&plugin_statistics, em);
665 netdata_mutex_unlock(&ebpf_exit_cleanup);
666 }
667
@@ -678,6 +734,9 @@ static void fd_apps_accumulator(netdata_fd_stat_t *out, int maps_per_core)
734 netdata_fd_stat_t *total = &out[0];
735 uint64_t ct = total->ct;
736 for (i = 1; i < end; i++) {
737 + if (ebpf_plugin_stop())
738 + break;
739 +
740 netdata_fd_stat_t *w = &out[i];
741 total->open_call += w->open_call;
742 total->close_call += w->close_call;
@@ -709,7 +768,11 @@ static void ebpf_read_fd_apps_table(int maps_per_core)
768
769 uint32_t key = 0, next_key = 0;
770 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
771 + if (ebpf_plugin_stop())
772 + break;
773 +
774 if (bpf_map_lookup_elem(fd, &key, fv)) {
775 + netdata_log_error("Failed to lookup PID %u in FD map", key);
776 goto end_fd_loop;
777 }
778
@@ -720,17 +783,15 @@ static void ebpf_read_fd_apps_table(int maps_per_core)
783 continue;
784 netdata_publish_fd_stat_t *publish_fd = &local_pid->fd;
785
723 - if (!publish_fd->ct || publish_fd->ct != fv->ct) {
786 + if (kill((pid_t)key, 0) == -1 && errno == ESRCH) {
787 + if (netdata_ebpf_reset_shm_pointer_unsafe(fd, key, NETDATA_EBPF_PIDS_FD_IDX))
788 + memset(publish_fd, 0, sizeof(*publish_fd));
789 + } else if (!publish_fd->ct || publish_fd->ct != fv->ct) {
790 publish_fd->ct = fv->ct;
791 publish_fd->open_call = fv->open_call;
792 publish_fd->close_call = fv->close_call;
793 publish_fd->open_err = fv->open_err;
794 publish_fd->close_err = fv->close_err;
729 - } else {
730 - if (kill((pid_t)key, 0)) { // No PID found
731 - if (netdata_ebpf_reset_shm_pointer_unsafe(fd, key, NETDATA_EBPF_PIDS_FD_IDX))
732 - memset(publish_fd, 0, sizeof(*publish_fd));
733 - }
795 }
796
797 end_fd_loop:
@@ -769,12 +830,15 @@ static void ebpf_fd_sum_pids(netdata_fd_stat_t *fd, struct ebpf_pid_on_target *r
830 /**
831 * Resume apps data
832 */
772 -void ebpf_fd_resume_apps_data()
833 +void ebpf_fd_resume_apps_data(void)
834 {
835 struct ebpf_target *w;
836
837 netdata_mutex_lock(&collect_data_mutex);
838 for (w = apps_groups_root_target; w; w = w->next) {
839 + if (ebpf_plugin_stop())
840 + break;
841 +
842 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_FD_IDX))))
843 continue;
844
@@ -790,12 +854,15 @@ void ebpf_fd_resume_apps_data()
854 *
855 * @param maps_per_core do I need to read all cores?
856 */
793 -static void ebpf_update_fd_cgroup()
857 +static void ebpf_update_fd_cgroup(void)
858 {
859 ebpf_cgroup_target_t *ect;
860
861 netdata_mutex_lock(&mutex_cgroup_shm);
862 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
863 + if (ebpf_plugin_stop())
864 + break;
865 +
866 struct pid_on_target2 *pids;
867 for (pids = ect->pids; pids; pids = pids->next) {
868 uint32_t pid = pids->pid;
@@ -814,9 +881,9 @@ static void ebpf_update_fd_cgroup()
881 }
882
883 /**
817 - * DCstat thread
884 + * FD thread
885 *
819 - * Thread used to generate dcstat charts.
886 + * Thread used to generate fd charts.
887 *
888 * @param ptr a pointer to `struct ebpf_module`
889 *
@@ -837,25 +904,47 @@ void ebpf_read_fd_thread(void *ptr)
904 uint32_t lifetime = em->lifetime;
905 int cgroups = em->cgroup_charts;
906 uint32_t running_time = 0;
840 - pids_fd[NETDATA_EBPF_PIDS_FD_IDX] = fd_maps[NETDATA_FD_PID_STATS].map_fd;
907 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_FD_IDX, fd_maps[NETDATA_FD_PID_STATS].map_fd);
908
909 heartbeat_t hb;
910 heartbeat_init(&hb, USEC_PER_SEC);
911 while (!ebpf_plugin_stop() && running_time < lifetime) {
912 + if (ebpf_plugin_stop())
913 + break;
914 +
915 heartbeat_next(&hb);
846 - if (ebpf_plugin_stop() || ++counter != update_every)
916 + if (ebpf_plugin_stop())
917 + break;
918 +
919 + if (++counter != update_every)
920 continue;
921
849 - sem_wait(shm_mutex_ebpf_integration);
922 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
923 + if (errno != ECANCELED)
924 + netdata_log_error("FD: Failed to wait on semaphore.");
925 + break;
926 + }
927 ebpf_read_fd_apps_table(maps_per_core);
928 ebpf_fd_resume_apps_data();
929 + if (ebpf_plugin_stop()) {
930 + if (sem_post(shm_mutex_ebpf_integration))
931 + netdata_log_error("FD: Failed to post semaphore.");
932 + break;
933 + }
934 +
935 if (cgroups && shm_ebpf_cgroup.header)
936 ebpf_update_fd_cgroup();
937
855 - sem_post(shm_mutex_ebpf_integration);
938 + if (sem_post(shm_mutex_ebpf_integration)) {
939 + netdata_log_error("FD: Failed to post semaphore.");
940 + break;
941 + }
942
943 counter = 0;
944
945 + if (ebpf_plugin_stop())
946 + break;
947 +
948 netdata_mutex_lock(&ebpf_exit_cleanup);
949 if (running_time && !em->running_time)
950 running_time = update_every;
@@ -878,6 +967,9 @@ void ebpf_fd_send_apps_data(ebpf_module_t *em, struct ebpf_target *root)
967 struct ebpf_target *w;
968 netdata_mutex_lock(&collect_data_mutex);
969 for (w = root; w; w = w->next) {
970 + if (ebpf_plugin_stop())
971 + break;
972 +
973 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_FD_IDX))))
974 continue;
975
@@ -928,10 +1020,10 @@ static void ebpf_fd_sum_cgroup_pids(netdata_publish_fd_stat_t *fd, struct pid_on
1020 pids = pids->next;
1021 }
1022
931 - fd->open_call = (accumulator.open_call >= fd->open_call) ? accumulator.open_call : fd->open_call;
932 - fd->open_err = (accumulator.open_err >= fd->open_err) ? accumulator.open_err : fd->open_err;
933 - fd->close_call = (accumulator.close_call >= fd->close_call) ? accumulator.close_call : fd->close_call;
934 - fd->close_err = (accumulator.close_err >= fd->close_err) ? accumulator.close_err : fd->close_err;
1023 + fd->open_call = accumulator.open_call;
1024 + fd->open_err = accumulator.open_err;
1025 + fd->close_call = accumulator.close_call;
1026 + fd->close_err = accumulator.close_err;
1027 }
1028
1029 /**
@@ -953,7 +1045,7 @@ static void ebpf_create_specific_fd_charts(char *type, ebpf_module_t *em)
1045 NETDATA_APPS_FILE_GROUP,
1046 NETDATA_CGROUP_FD_OPEN_CONTEXT,
1047 NETDATA_EBPF_CHART_TYPE_LINE,
956 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5400,
1048 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN,
1049 ebpf_create_global_dimension,
1050 &fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN],
1051 1,
@@ -971,7 +1063,7 @@ static void ebpf_create_specific_fd_charts(char *type, ebpf_module_t *em)
1063 NETDATA_APPS_FILE_GROUP,
1064 NETDATA_CGROUP_FD_OPEN_ERR_CONTEXT,
1065 NETDATA_EBPF_CHART_TYPE_LINE,
974 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5401,
1066 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN_ERR,
1067 ebpf_create_global_dimension,
1068 &fd_publish_aggregated[NETDATA_FD_SYSCALL_OPEN],
1069 1,
@@ -989,7 +1081,7 @@ static void ebpf_create_specific_fd_charts(char *type, ebpf_module_t *em)
1081 NETDATA_APPS_FILE_GROUP,
1082 NETDATA_CGROUP_FD_CLOSE_CONTEXT,
1083 NETDATA_EBPF_CHART_TYPE_LINE,
992 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5402,
1084 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE,
1085 ebpf_create_global_dimension,
1086 &fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE],
1087 1,
@@ -1007,7 +1099,7 @@ static void ebpf_create_specific_fd_charts(char *type, ebpf_module_t *em)
1099 NETDATA_APPS_FILE_GROUP,
1100 NETDATA_CGROUP_FD_CLOSE_ERR_CONTEXT,
1101 NETDATA_EBPF_CHART_TYPE_LINE,
1010 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5403,
1102 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE_ERR,
1103 ebpf_create_global_dimension,
1104 &fd_publish_aggregated[NETDATA_FD_SYSCALL_CLOSE],
1105 1,
@@ -1037,7 +1129,7 @@ static void ebpf_obsolete_specific_fd_charts(char *type, ebpf_module_t *em)
1129 NETDATA_APPS_FILE_GROUP,
1130 NETDATA_EBPF_CHART_TYPE_LINE,
1131 NETDATA_CGROUP_FD_OPEN_CONTEXT,
1040 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5400,
1132 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN,
1133 em->update_every);
1134
1135 if (em->mode < MODE_ENTRY) {
@@ -1050,7 +1142,7 @@ static void ebpf_obsolete_specific_fd_charts(char *type, ebpf_module_t *em)
1142 NETDATA_APPS_FILE_GROUP,
1143 NETDATA_EBPF_CHART_TYPE_LINE,
1144 NETDATA_CGROUP_FD_OPEN_ERR_CONTEXT,
1053 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5401,
1145 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN_ERR,
1146 em->update_every);
1147 }
1148
@@ -1063,7 +1155,7 @@ static void ebpf_obsolete_specific_fd_charts(char *type, ebpf_module_t *em)
1155 NETDATA_APPS_FILE_GROUP,
1156 NETDATA_EBPF_CHART_TYPE_LINE,
1157 NETDATA_CGROUP_FD_CLOSE_CONTEXT,
1066 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5402,
1158 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE,
1159 em->update_every);
1160
1161 if (em->mode < MODE_ENTRY) {
@@ -1076,7 +1168,7 @@ static void ebpf_obsolete_specific_fd_charts(char *type, ebpf_module_t *em)
1168 NETDATA_APPS_FILE_GROUP,
1169 NETDATA_EBPF_CHART_TYPE_LINE,
1170 NETDATA_CGROUP_FD_CLOSE_ERR_CONTEXT,
1079 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5403,
1171 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE_ERR,
1172 em->update_every);
1173 }
1174 }
@@ -1126,7 +1218,7 @@ static void ebpf_create_systemd_fd_charts(ebpf_module_t *em)
1218 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1219 .family = NETDATA_APPS_FILE_GROUP,
1220 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1129 - .order = 20270,
1221 + .order = NETDATA_EBPF_FD_CHART_PRIORITY_OPEN,
1222 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1223 .context = NETDATA_SYSTEMD_FD_OPEN_CONTEXT,
1224 .module = NETDATA_EBPF_MODULE_NAME_FD,
@@ -1139,7 +1231,7 @@ static void ebpf_create_systemd_fd_charts(ebpf_module_t *em)
1231 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1232 .family = NETDATA_APPS_FILE_GROUP,
1233 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1142 - .order = 20271,
1234 + .order = NETDATA_EBPF_FD_CHART_PRIORITY_OPEN_ERR,
1235 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1236 .context = NETDATA_SYSTEMD_FD_OPEN_ERR_CONTEXT,
1237 .module = NETDATA_EBPF_MODULE_NAME_FD,
@@ -1152,7 +1244,7 @@ static void ebpf_create_systemd_fd_charts(ebpf_module_t *em)
1244 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1245 .family = NETDATA_APPS_FILE_GROUP,
1246 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1155 - .order = 20272,
1247 + .order = NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE,
1248 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1249 .context = NETDATA_SYSTEMD_FD_CLOSE_CONTEXT,
1250 .module = NETDATA_EBPF_MODULE_NAME_FD,
@@ -1165,21 +1257,27 @@ static void ebpf_create_systemd_fd_charts(ebpf_module_t *em)
1257 .units = EBPF_COMMON_UNITS_CALLS_PER_SEC,
1258 .family = NETDATA_APPS_FILE_GROUP,
1259 .charttype = NETDATA_EBPF_CHART_TYPE_STACKED,
1168 - .order = 20273,
1260 + .order = NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE_ERR,
1261 .algorithm = EBPF_CHART_ALGORITHM_INCREMENTAL,
1170 - .context = NETDATA_SYSTEMD_FD_OPEN_ERR_CONTEXT,
1262 + .context = NETDATA_SYSTEMD_FD_CLOSE_ERR_CONTEXT,
1263 .module = NETDATA_EBPF_MODULE_NAME_FD,
1264 .update_every = 0,
1265 .suffix = NETDATA_SYSCALL_APPS_FILE_CLOSE_ERROR,
1266 .dimension = "calls"};
1267
1176 - if (!data_open.update_every)
1177 - data_open.update_every = data_open_error.update_every = data_close.update_every =
1178 - data_close_error.update_every = em->update_every;
1268 + if (!data_open.update_every) {
1269 + data_open.update_every = em->update_every;
1270 + data_open_error.update_every = em->update_every;
1271 + data_close.update_every = em->update_every;
1272 + data_close_error.update_every = em->update_every;
1273 + }
1274
1275 ebpf_cgroup_target_t *w;
1276 netdata_run_mode_t mode = em->mode;
1277 for (w = ebpf_cgroup_pids; w; w = w->next) {
1278 + if (ebpf_plugin_stop())
1279 + break;
1280 +
1281 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_FD_CHART))
1282 continue;
1283
@@ -1206,6 +1304,9 @@ static void ebpf_send_systemd_fd_charts(ebpf_module_t *em)
1304 {
1305 ebpf_cgroup_target_t *ect;
1306 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1307 + if (ebpf_plugin_stop())
1308 + break;
1309 +
1310 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_FD_CHART))) {
1311 continue;
1312 }
@@ -1245,6 +1346,11 @@ static void ebpf_fd_send_cgroup_data(ebpf_module_t *em)
1346 ebpf_fd_sum_cgroup_pids(&ect->publish_systemd_fd, ect->pids);
1347 }
1348
1349 + if (ebpf_plugin_stop()) {
1350 + netdata_mutex_unlock(&mutex_cgroup_shm);
1351 + return;
1352 + }
1353 +
1354 if (shm_ebpf_cgroup.header->systemd_enabled) {
1355 if (send_cgroup_chart) {
1356 ebpf_create_systemd_fd_charts(em);
@@ -1254,6 +1360,9 @@ static void ebpf_fd_send_cgroup_data(ebpf_module_t *em)
1360 }
1361
1362 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1363 + if (ebpf_plugin_stop())
1364 + break;
1365 +
1366 if (ect->systemd)
1367 continue;
1368
@@ -1291,15 +1400,24 @@ static void fd_collector(ebpf_module_t *em)
1400 heartbeat_t hb;
1401 heartbeat_init(&hb, USEC_PER_SEC);
1402 while (!ebpf_plugin_stop() && running_time < lifetime) {
1403 + if (ebpf_plugin_stop())
1404 + break;
1405 +
1406 heartbeat_next(&hb);
1407
1296 - if (ebpf_plugin_stop() || ++counter != update_every)
1408 + if (ebpf_plugin_stop())
1409 + break;
1410 +
1411 + if (++counter != update_every)
1412 continue;
1413
1414 counter = 0;
1415 netdata_apps_integration_flags_t apps = em->apps_charts;
1416 ebpf_fd_read_global_tables(stats, maps_per_core);
1417
1418 + if (ebpf_plugin_stop())
1419 + break;
1420 +
1421 netdata_mutex_lock(&lock);
1422
1423 ebpf_fd_send_data(em);
@@ -1307,11 +1425,19 @@ static void fd_collector(ebpf_module_t *em)
1425 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
1426 ebpf_fd_send_apps_data(em, apps_groups_root_target);
1427
1428 + if (ebpf_plugin_stop()) {
1429 + netdata_mutex_unlock(&lock);
1430 + break;
1431 + }
1432 +
1433 if (cgroups && shm_ebpf_cgroup.header)
1434 ebpf_fd_send_cgroup_data(em);
1435
1436 netdata_mutex_unlock(&lock);
1437
1438 + if (ebpf_plugin_stop())
1439 + break;
1440 +
1441 netdata_mutex_lock(&ebpf_exit_cleanup);
1442 if (running_time && !em->running_time)
1443 running_time = update_every;
@@ -1342,6 +1468,9 @@ void ebpf_fd_create_apps_charts(struct ebpf_module *em, void *ptr)
1468 struct ebpf_target *w;
1469 int update_every = em->update_every;
1470 for (w = root; w; w = w->next) {
1471 + if (ebpf_plugin_stop())
1472 + break;
1473 +
1474 if (unlikely(!w->exposed))
1475 continue;
1476
@@ -1405,7 +1534,7 @@ void ebpf_fd_create_apps_charts(struct ebpf_module *em, void *ptr)
1534 NETDATA_APPS_FILE_FDS,
1535 NETDATA_EBPF_CHART_TYPE_STACKED,
1536 "app.ebpf_file_close_error",
1408 - 20223,
1537 + NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE_ERR,
1538 update_every,
1539 NETDATA_EBPF_MODULE_NAME_FD);
1540 ebpf_create_chart_labels("app_group", w->name, RRDLABEL_SRC_AUTO);
@@ -1475,7 +1604,7 @@ static void ebpf_create_fd_global_charts(ebpf_module_t *em)
1604 * We are not testing the return, because callocz does this and shutdown the software
1605 * case it was not possible to allocate.
1606 */
1478 -static inline void ebpf_fd_allocate_global_vectors()
1607 +static inline void ebpf_fd_allocate_global_vectors(void)
1608 {
1609 fd_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_fd_stat_t));
1610 fd_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
@@ -1507,8 +1636,13 @@ static int ebpf_fd_load_bpf(ebpf_module_t *em)
1636 fd_bpf_obj = fd_bpf__open();
1637 if (!fd_bpf_obj)
1638 ret = -1;
1510 - else
1639 + else {
1640 ret = ebpf_fd_load_and_attach(fd_bpf_obj, em);
1641 + if (ret) {
1642 + fd_bpf__destroy(fd_bpf_obj);
1643 + fd_bpf_obj = NULL;
1644 + }
1645 + }
1646 }
1647 #endif
1648
@@ -1531,8 +1665,14 @@ void ebpf_fd_thread(void *ptr)
1665 {
1666 ebpf_module_t *em = (ebpf_module_t *)ptr;
1667
1668 + fd_safe_clean = false;
1669 +
1670 CLEANUP_FUNCTION_REGISTER(ebpf_fd_exit) cleanup_ptr = em;
1671
1672 + if (!ebpf_module_thread_has_valid_state(em)) {
1673 + goto endfd;
1674 + }
1675 +
1676 em->maps = fd_maps;
1677
1678 #ifdef LIBBPF_MAJOR_VERSION
@@ -1558,6 +1698,7 @@ void ebpf_fd_thread(void *ptr)
1698
1699 ebpf_read_fd.thread = nd_thread_create(ebpf_read_fd.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_fd_thread, em);
1700
1701 + fd_safe_clean = true;
1702 fd_collector(em);
1703
1704 endfd:
src/collectors/ebpf.plugin/ebpf_fd.h
+22 -2
@@ -3,6 +3,10 @@
3 #ifndef NETDATA_EBPF_FD_H
4 #define NETDATA_EBPF_FD_H 1
5
6 +#include "libnetdata/libnetdata.h"
7 +#include "collectors/collectors-ipc/ebpf-ipc.h"
8 +#include "libbpf_api/ebpf.h"
9 +
10 // Module name & File description
11 #define NETDATA_EBPF_MODULE_NAME_FD "filedescriptor"
12 #define NETDATA_EBPF_FD_MODULE_DESC \
@@ -38,6 +42,22 @@
42 #define NETDATA_SYSTEMD_FD_CLOSE_CONTEXT "systemd.service.fd_close"
43 #define NETDATA_SYSTEMD_FD_CLOSE_ERR_CONTEXT "systemd.service.fd_close_error"
44
45 +// Chart priorities
46 +#define NETDATA_EBPF_FD_CHART_PRIORITY_OPEN 20270
47 +#define NETDATA_EBPF_FD_CHART_PRIORITY_OPEN_ERR 20271
48 +#define NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE 20272
49 +#define NETDATA_EBPF_FD_CHART_PRIORITY_CLOSE_ERR 20273
50 +
51 +#define NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN 5400
52 +#define NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_OPEN_ERR 5401
53 +#define NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE 5402
54 +#define NETDATA_EBPF_FD_CHART_PRIORITY_CGROUP_CLOSE_ERR 5403
55 +
56 +#define NETDATA_EBPF_FD_CHART_PRIORITY_APPS_OPEN 20220
57 +#define NETDATA_EBPF_FD_CHART_PRIORITY_APPS_OPEN_ERR 20221
58 +#define NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE 20222
59 +#define NETDATA_EBPF_FD_CHART_PRIORITY_APPS_CLOSE_ERR 20223
60 +
61 // ARAL name
62 #define NETDATA_EBPF_FD_ARAL_NAME "ebpf_fd"
63
@@ -64,7 +84,7 @@ enum fd_syscalls {
84 NETDATA_FD_SYSCALL_OPEN,
85 NETDATA_FD_SYSCALL_CLOSE,
86
67 - // Do not insert nothing after this value
87 + // Keep this as last and don't skip numbers as it is used as element counter
88 NETDATA_FD_SYSCALL_END
89 };
90
@@ -72,6 +92,7 @@ enum fd_close_syscall {
92 NETDATA_FD_CLOSE_FD,
93 NETDATA_FD___CLOSE_FD,
94
95 + // Keep this as last and don't skip numbers as it is used as element counter
96 NETDATA_FD_CLOSE_END
97 };
98
@@ -79,7 +100,6 @@ enum fd_close_syscall {
100
101 void ebpf_fd_thread(void *ptr);
102 void ebpf_fd_create_apps_charts(struct ebpf_module *em, void *ptr);
82 -void ebpf_fd_release(netdata_fd_stat_t *stat);
103 extern struct config fd_config;
104 extern netdata_ebpf_targets_t fd_targets[];
105
src/collectors/ebpf.plugin/ebpf_filesystem.c
+180 -153
@@ -1,6 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "ebpf_filesystem.h"
4 +#include "libbpf_api/ebpf_library.h"
5
6 struct config fs_config = APPCONFIG_INITIALIZER;
7
@@ -15,8 +16,8 @@ ebpf_local_maps_t ext4_maps[] = {
16 #endif
17 },
18 {.name = "tmp_ext4",
18 - .internal_input = 4192,
19 - .user_input = 4192,
19 + .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
20 + .user_input = NETDATA_FS_TEMP_MAP_SIZE,
21 .type = NETDATA_EBPF_MAP_CONTROLLER,
22 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
23 #ifdef LIBBPF_MAJOR_VERSION
@@ -44,8 +45,8 @@ ebpf_local_maps_t xfs_maps[] = {
45 #endif
46 },
47 {.name = "tmp_xfs",
47 - .internal_input = 4192,
48 - .user_input = 4192,
48 + .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
49 + .user_input = NETDATA_FS_TEMP_MAP_SIZE,
50 .type = NETDATA_EBPF_MAP_CONTROLLER,
51 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
52 #ifdef LIBBPF_MAJOR_VERSION
@@ -73,8 +74,8 @@ ebpf_local_maps_t nfs_maps[] = {
74 #endif
75 },
76 {.name = "tmp_nfs",
76 - .internal_input = 4192,
77 - .user_input = 4192,
77 + .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
78 + .user_input = NETDATA_FS_TEMP_MAP_SIZE,
79 .type = NETDATA_EBPF_MAP_CONTROLLER,
80 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
81 #ifdef LIBBPF_MAJOR_VERSION
@@ -102,8 +103,8 @@ ebpf_local_maps_t zfs_maps[] = {
103 #endif
104 },
105 {.name = "tmp_zfs",
105 - .internal_input = 4192,
106 - .user_input = 4192,
106 + .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
107 + .user_input = NETDATA_FS_TEMP_MAP_SIZE,
108 .type = NETDATA_EBPF_MAP_CONTROLLER,
109 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
110 #ifdef LIBBPF_MAJOR_VERSION
@@ -140,8 +141,8 @@ ebpf_local_maps_t btrfs_maps[] = {
141 #endif
142 },
143 {.name = "tmp_btrfs",
143 - .internal_input = 4192,
144 - .user_input = 4192,
144 + .internal_input = NETDATA_FS_TEMP_MAP_SIZE,
145 + .user_input = NETDATA_FS_TEMP_MAP_SIZE,
146 .type = NETDATA_EBPF_MAP_CONTROLLER,
147 .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
148 #ifdef LIBBPF_MAJOR_VERSION
@@ -264,52 +265,90 @@ static int ebpf_fs_attach_kprobe(struct filesystem_bpf *obj, const char **functi
265 obj->links.netdata_fs_file_write_probe =
266 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_write_probe, false, functions[NETDATA_KEY_BTF_WRITE]);
267 if (libbpf_get_error(obj->links.netdata_fs_file_write_probe))
267 - return -1;
268 + goto cleanup_file_read;
269
270 obj->links.netdata_fs_file_open_probe =
271 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_open_probe, false, functions[NETDATA_KEY_BTF_OPEN]);
272 if (libbpf_get_error(obj->links.netdata_fs_file_open_probe))
272 - return -1;
273 + goto cleanup_file_write;
274
275 obj->links.netdata_fs_getattr_probe =
276 bpf_program__attach_kprobe(obj->progs.netdata_fs_getattr_probe, false, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
277 if (libbpf_get_error(obj->links.netdata_fs_getattr_probe))
277 - return -1;
278 + goto cleanup_file_open;
279
280 // kretprobe
281 obj->links.netdata_fs_file_read_retprobe =
282 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_read_retprobe, false, functions[NETDATA_KEY_BTF_READ]);
283 if (libbpf_get_error(obj->links.netdata_fs_file_read_retprobe))
283 - return -1;
284 + goto cleanup_getattr;
285
286 obj->links.netdata_fs_file_write_retprobe =
287 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_write_retprobe, false, functions[NETDATA_KEY_BTF_WRITE]);
288 if (libbpf_get_error(obj->links.netdata_fs_file_write_retprobe))
288 - return -1;
289 + goto cleanup_file_read_ret;
290
291 obj->links.netdata_fs_file_open_retprobe =
292 bpf_program__attach_kprobe(obj->progs.netdata_fs_file_open_retprobe, false, functions[NETDATA_KEY_BTF_OPEN]);
293 if (libbpf_get_error(obj->links.netdata_fs_file_open_retprobe))
293 - return -1;
294 + goto cleanup_file_write_ret;
295
296 obj->links.netdata_fs_getattr_retprobe =
297 bpf_program__attach_kprobe(obj->progs.netdata_fs_getattr_retprobe, false, functions[NETDATA_KEY_BTF_SYNC_ATTR]);
298 if (libbpf_get_error(obj->links.netdata_fs_getattr_retprobe))
298 - return -1;
299 + goto cleanup_file_open_ret;
300
301 if (functions[NETDATA_KEY_BTF_OPEN2]) {
302 obj->links.netdata_fs_2nd_file_open_probe = bpf_program__attach_kprobe(
303 obj->progs.netdata_fs_2nd_file_open_probe, false, functions[NETDATA_KEY_BTF_OPEN2]);
304 if (libbpf_get_error(obj->links.netdata_fs_2nd_file_open_probe))
304 - return -1;
305 + goto cleanup_getattr_ret;
306
307 obj->links.netdata_fs_2nd_file_open_retprobe = bpf_program__attach_kprobe(
308 obj->progs.netdata_fs_2nd_file_open_retprobe, false, functions[NETDATA_KEY_BTF_OPEN2]);
309 if (libbpf_get_error(obj->links.netdata_fs_2nd_file_open_retprobe))
309 - return -1;
310 + goto cleanup_2nd_open_probe;
311 }
312
313 return 0;
314 +
315 +cleanup_2nd_open_probe:
316 + if (obj->links.netdata_fs_2nd_file_open_probe)
317 + bpf_link__destroy(obj->links.netdata_fs_2nd_file_open_probe);
318 +
319 +cleanup_getattr_ret:
320 + if (obj->links.netdata_fs_getattr_retprobe)
321 + bpf_link__destroy(obj->links.netdata_fs_getattr_retprobe);
322 +
323 +cleanup_file_open_ret:
324 + if (obj->links.netdata_fs_file_open_retprobe)
325 + bpf_link__destroy(obj->links.netdata_fs_file_open_retprobe);
326 +
327 +cleanup_file_write_ret:
328 + if (obj->links.netdata_fs_file_write_retprobe)
329 + bpf_link__destroy(obj->links.netdata_fs_file_write_retprobe);
330 +
331 +cleanup_file_read_ret:
332 + if (obj->links.netdata_fs_file_read_retprobe)
333 + bpf_link__destroy(obj->links.netdata_fs_file_read_retprobe);
334 +
335 +cleanup_getattr:
336 + if (obj->links.netdata_fs_getattr_probe)
337 + bpf_link__destroy(obj->links.netdata_fs_getattr_probe);
338 +
339 +cleanup_file_open:
340 + if (obj->links.netdata_fs_file_open_probe)
341 + bpf_link__destroy(obj->links.netdata_fs_file_open_probe);
342 +
343 +cleanup_file_write:
344 + if (obj->links.netdata_fs_file_write_probe)
345 + bpf_link__destroy(obj->links.netdata_fs_file_write_probe);
346 +
347 +cleanup_file_read:
348 + if (obj->links.netdata_fs_file_read_probe)
349 + bpf_link__destroy(obj->links.netdata_fs_file_read_probe);
350 +
351 + return -1;
352 }
353
354 /**
@@ -356,43 +395,22 @@ ebpf_fs_load_and_attach(ebpf_local_maps_t *map, struct filesystem_bpf *obj, cons
395 *
396 *****************************************************************/
397
359 -/**
360 - * Obsolete Cleanup Struct
361 - *
362 - * Clean allocatged data durinc obsolete steps
363 - *
364 - * @param efp
365 - */
366 -static void ebpf_obsolete_cleanup_struct(ebpf_filesystem_partitions_t *efp)
398 +static void ebpf_cleanup_fs_histogram(netdata_ebpf_histogram_t *hist)
399 {
368 - freez(efp->hread.name);
369 - efp->hread.name = NULL;
370 - freez(efp->hread.title);
371 - efp->hread.title = NULL;
372 - freez(efp->hread.ctx);
373 - efp->hread.ctx = NULL;
374 -
375 - freez(efp->hwrite.name);
376 - efp->hwrite.name = NULL;
377 - freez(efp->hwrite.title);
378 - efp->hwrite.title = NULL;
379 - freez(efp->hwrite.ctx);
380 - efp->hwrite.ctx = NULL;
381 -
382 - freez(efp->hopen.name);
383 - efp->hopen.name = NULL;
384 - freez(efp->hopen.title);
385 - efp->hopen.title = NULL;
386 - freez(efp->hopen.ctx);
387 - efp->hopen.ctx = NULL;
388 -
389 - freez(efp->hadditional.name);
390 - efp->hadditional.name = NULL;
391 - freez(efp->hadditional.title);
392 - efp->hadditional.title = NULL;
393 - freez(efp->hadditional.ctx);
394 - efp->hadditional.ctx = NULL;
400 + freez(hist->name);
401 + hist->name = NULL;
402 + freez(hist->title);
403 + hist->title = NULL;
404 + freez(hist->ctx);
405 + hist->ctx = NULL;
406 +}
407
408 +static void ebpf_cleanup_fs_partition(ebpf_filesystem_partitions_t *efp)
409 +{
410 + ebpf_cleanup_fs_histogram(&efp->hread);
411 + ebpf_cleanup_fs_histogram(&efp->hwrite);
412 + ebpf_cleanup_fs_histogram(&efp->hopen);
413 + ebpf_cleanup_fs_histogram(&efp->hadditional);
414 freez(efp->family_name);
415 efp->family_name = NULL;
416 }
@@ -462,7 +480,7 @@ static void ebpf_obsolete_fs_charts(int update_every)
480 efp->hadditional.order,
481 update_every);
482
465 - ebpf_obsolete_cleanup_struct(efp);
483 + ebpf_cleanup_fs_partition(efp);
484 }
485 efp->flags = flags;
486 }
@@ -625,11 +643,15 @@ int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
643 if (!efp->fs_obj) {
644 em->info.thread_name = saved_name;
645 em->kernels = kernels;
646 + em->maps = NULL;
647 netdata_mutex_unlock(&lock);
648 return -1;
649 } else if (ebpf_fs_load_and_attach(em->maps, efp->fs_obj, efp->functions, NULL)) {
650 + filesystem_bpf__destroy(efp->fs_obj);
651 + efp->fs_obj = NULL;
652 em->info.thread_name = saved_name;
653 em->kernels = kernels;
654 + em->maps = NULL;
655 netdata_mutex_unlock(&lock);
656 return -1;
657 }
@@ -638,9 +660,10 @@ int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
660 efp->flags |= NETDATA_FILESYSTEM_FLAG_HAS_PARTITION;
661 ebpf_update_kernel_memory(&plugin_statistics, efp->fs_maps, EBPF_ACTION_STAT_ADD);
662
641 - // Nedeed for filesystems like btrfs
663 + // Needed for filesystems like btrfs
664 if ((efp->flags & NETDATA_FILESYSTEM_FILL_ADDRESS_TABLE) && (efp->addresses.function)) {
643 - ebpf_load_addresses(&efp->addresses, efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd);
665 + if (efp->fs_maps && efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd >= 0)
666 + ebpf_load_addresses(&efp->addresses, efp->fs_maps[NETDATA_ADDR_FS_TABLE].map_fd);
667 }
668 }
669 efp->flags &= ~NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM;
@@ -667,7 +690,7 @@ int ebpf_filesystem_initialize_ebpf_data(ebpf_module_t *em)
690 *
691 * @return the total of partitions that will be monitored
692 */
670 -static int ebpf_read_local_partitions()
693 +static int ebpf_read_local_partitions(void)
694 {
695 char filename[FILENAME_MAX + 1];
696 snprintfz(filename, FILENAME_MAX, "%s/proc/self/mountinfo", netdata_configured_host_prefix);
@@ -731,7 +754,7 @@ static int ebpf_update_partitions(ebpf_module_t *em)
754 if (curr < update_every)
755 return 0;
756
734 - update_every = curr + 5 * em->update_every;
757 + update_every = curr + (NETDATA_PARTITION_UPDATE_INTERVAL_MULTIPLIER * em->update_every);
758 if (!ebpf_read_local_partitions()) {
759 em->optional = -1;
760 return -1;
@@ -753,57 +776,16 @@ static int ebpf_update_partitions(ebpf_module_t *em)
776 /*
777 * Cleanup eBPF data
778 */
756 -void ebpf_filesystem_cleanup_ebpf_data()
779 +void ebpf_filesystem_cleanup_ebpf_data(void)
780 {
781 int i;
782 for (i = 0; localfs[i].filesystem; i++) {
783 ebpf_filesystem_partitions_t *efp = &localfs[i];
761 - if (efp->probe_links) {
762 - freez(efp->family_name);
763 - efp->family_name = NULL;
764 -
765 - freez(efp->hread.name);
766 - efp->hread.name = NULL;
767 - freez(efp->hread.title);
768 - efp->hread.title = NULL;
769 -
770 - freez(efp->hwrite.name);
771 - efp->hwrite.name = NULL;
772 - freez(efp->hwrite.title);
773 - efp->hwrite.title = NULL;
774 -
775 - freez(efp->hopen.name);
776 - efp->hopen.name = NULL;
777 - freez(efp->hopen.title);
778 - efp->hopen.title = NULL;
779 -
780 - freez(efp->hadditional.name);
781 - efp->hadditional.name = NULL;
782 - freez(efp->hadditional.title);
783 - efp->hadditional.title = NULL;
784 - freez(efp->hadditional.ctx);
785 - efp->hadditional.ctx = NULL;
786 - }
784 + if (efp->probe_links)
785 + ebpf_cleanup_fs_partition(efp);
786 }
787 }
788
790 -/**
791 - * Cleanup FS Histograms
792 - *
793 - * @param ptr pointer to structure to be cleaned
794 - */
795 -static void ebpf_cleanup_fs_histograms(netdata_ebpf_histogram_t *ptr)
796 -{
797 - freez(ptr->name);
798 - ptr->name = NULL;
799 -
800 - freez(ptr->title);
801 - ptr->title = NULL;
802 -
803 - freez(ptr->ctx);
804 - ptr->ctx = NULL;
805 -}
806 -
789 /**
790 * Obsolete global
791 *
@@ -819,6 +801,9 @@ static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
801 if (!efp->objects)
802 continue;
803
804 + if (!(efp->flags & NETDATA_FILESYSTEM_FLAG_CHART_CREATED))
805 + continue;
806 +
807 ebpf_write_chart_obsolete(
808 NETDATA_FILESYSTEM_FAMILY,
809 efp->hread.name,
@@ -830,7 +815,7 @@ static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
815 "filesystem.read_latency",
816 efp->hread.order,
817 em->update_every);
833 - ebpf_cleanup_fs_histograms(&efp->hread);
818 + ebpf_cleanup_fs_histogram(&efp->hread);
819
820 ebpf_write_chart_obsolete(
821 NETDATA_FILESYSTEM_FAMILY,
@@ -843,7 +828,7 @@ static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
828 "filesystem.write_latency",
829 efp->hwrite.order,
830 em->update_every);
846 - ebpf_cleanup_fs_histograms(&efp->hwrite);
831 + ebpf_cleanup_fs_histogram(&efp->hwrite);
832
833 ebpf_write_chart_obsolete(
834 NETDATA_FILESYSTEM_FAMILY,
@@ -856,7 +841,7 @@ static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
841 "filesystem.open_latency",
842 efp->hopen.order,
843 em->update_every);
859 - ebpf_cleanup_fs_histograms(&efp->hopen);
844 + ebpf_cleanup_fs_histogram(&efp->hopen);
845
846 ebpf_write_chart_obsolete(
847 NETDATA_FILESYSTEM_FAMILY,
@@ -869,7 +854,7 @@ static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
854 efp->hadditional.ctx,
855 efp->hadditional.order,
856 em->update_every);
872 - ebpf_cleanup_fs_histograms(&efp->hadditional);
857 + ebpf_cleanup_fs_histogram(&efp->hadditional);
858
859 efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
860 }
@@ -882,13 +867,50 @@ static void ebpf_obsolete_filesystem_global(ebpf_module_t *em)
867 *
868 * @param ptr thread data.
869 */
870 +void ebpf_filesystem_unload_bpf(ebpf_module_t *em)
871 +{
872 + int i;
873 + for (i = 0; localfs[i].filesystem; i++) {
874 + ebpf_filesystem_partitions_t *efp = &localfs[i];
875 +#ifdef LIBBPF_MAJOR_VERSION
876 + if (efp->fs_obj) {
877 + filesystem_bpf__destroy(efp->fs_obj);
878 + efp->fs_obj = NULL;
879 + efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
880 + }
881 +#endif
882 + if ((em->load & EBPF_LOAD_LEGACY) && efp->probe_links) {
883 + ebpf_unload_legacy_code(efp->objects, efp->probe_links);
884 + efp->objects = NULL;
885 + efp->probe_links = NULL;
886 + efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
887 + }
888 + }
889 +}
890 +
891 static void ebpf_filesystem_exit(void *pptr)
892 {
893 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
894 if (!em)
895 return;
896
891 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
897 + int i;
898 + bool has_resources = false;
899 + for (i = 0; localfs[i].filesystem; i++) {
900 + ebpf_filesystem_partitions_t *efp = &localfs[i];
901 + if (efp->probe_links || efp->objects || efp->fs_obj) {
902 + has_resources = true;
903 + break;
904 + }
905 + }
906 + if (!dimensions && !filesystem_hash_values && !has_resources) {
907 + netdata_mutex_lock(&ebpf_exit_cleanup);
908 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
909 + netdata_mutex_unlock(&ebpf_exit_cleanup);
910 + return;
911 + }
912 +
913 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
914 netdata_mutex_lock(&lock);
915 ebpf_obsolete_filesystem_global(em);
916
@@ -904,21 +926,11 @@ static void ebpf_filesystem_exit(void *pptr)
926
927 freez(filesystem_hash_values);
928
907 - int i;
908 - for (i = 0; localfs[i].filesystem; i++) {
909 - ebpf_filesystem_partitions_t *efp = &localfs[i];
910 - if (!efp->probe_links)
911 - continue;
912 -
913 - ebpf_unload_legacy_code(efp->objects, efp->probe_links);
914 - efp->objects = NULL;
915 - efp->probe_links = NULL;
916 - efp->flags = NETDATA_FILESYSTEM_FLAG_NO_PARTITION;
917 - }
929 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
930 + em->functions.bpf_unload(em);
931
932 netdata_mutex_lock(&ebpf_exit_cleanup);
933 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
921 - ebpf_update_stats(&plugin_statistics, em);
934 netdata_mutex_unlock(&ebpf_exit_cleanup);
935 }
936
@@ -929,36 +941,37 @@ static void ebpf_filesystem_exit(void *pptr)
941 *****************************************************************/
942
943 /**
932 - * Select hist
944 + * Select histogram
945 *
934 - * Select a histogram to store data.
946 + * Select histogram based on operation type
947 *
936 - * @param efp pointer for the structure with pointers.
948 + * @param efp pointer to filesystem partition
949 * @param id histogram selector
950 *
939 - * @return It returns a pointer for the histogram
951 + * @return pointer to histogram and sets idx
952 */
953 static inline netdata_ebpf_histogram_t *select_hist(ebpf_filesystem_partitions_t *efp, uint32_t *idx, uint32_t id)
954 {
943 - if (id < NETDATA_KEY_CALLS_READ) {
944 - *idx = id;
945 - return &efp->hread;
946 - } else if (id < NETDATA_KEY_CALLS_WRITE) {
947 - *idx = id - NETDATA_KEY_CALLS_READ;
948 - return &efp->hwrite;
949 - } else if (id < NETDATA_KEY_CALLS_OPEN) {
950 - *idx = id - NETDATA_KEY_CALLS_WRITE;
951 - return &efp->hopen;
952 - } else if (id < NETDATA_KEY_CALLS_SYNC) {
953 - *idx = id - NETDATA_KEY_CALLS_OPEN;
954 - return &efp->hadditional;
955 + uint32_t hist_idx = id / NETDATA_FS_HISTOGRAM_BINS;
956 + if (hist_idx >= 4)
957 + return NULL;
958 +
959 + *idx = id % NETDATA_FS_HISTOGRAM_BINS;
960 +
961 + switch (hist_idx) {
962 + case 0:
963 + return &efp->hread;
964 + case 1:
965 + return &efp->hwrite;
966 + case 2:
967 + return &efp->hopen;
968 + default:
969 + return &efp->hadditional;
970 }
956 -
957 - return NULL;
971 }
972
973 /**
961 - * Read hard disk table
974 + * Read filesystem table
975 *
976 * @param efp structure with filesystem monitored
977 * @param fd file descriptor to get data.
@@ -968,6 +981,9 @@ static inline netdata_ebpf_histogram_t *select_hist(ebpf_filesystem_partitions_t
981 */
982 static void read_filesystem_table(ebpf_filesystem_partitions_t *efp, int fd, int maps_per_core)
983 {
984 + if (!efp || !efp->fs_maps)
985 + return;
986 +
987 netdata_idx_t *values = filesystem_hash_values;
988 uint32_t key;
989 uint32_t idx;
@@ -996,7 +1012,7 @@ static void read_filesystem_table(ebpf_filesystem_partitions_t *efp, int fd, int
1012 }
1013
1014 /**
999 - * Read hard disk table
1015 + * Read filesystem table
1016 *
1017 * Read the table with number of calls for all functions
1018 *
@@ -1007,17 +1023,17 @@ static void read_filesystem_tables(int maps_per_core)
1023 int i;
1024 for (i = 0; localfs[i].filesystem; i++) {
1025 ebpf_filesystem_partitions_t *efp = &localfs[i];
1010 - if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION) {
1026 + if (efp->flags & NETDATA_FILESYSTEM_FLAG_HAS_PARTITION && efp->fs_maps) {
1027 read_filesystem_table(efp, efp->fs_maps[NETDATA_MAIN_FS_TABLE].map_fd, maps_per_core);
1028 }
1029 }
1030 }
1031
1032 /**
1017 - * Socket read hash
1033 + * Filesystem read hash
1034 *
1035 * This is the thread callback.
1020 - * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
1036 + * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy filesystem.
1037 *
1038 * @param ptr It is a NULL value for this thread.
1039 *
@@ -1040,7 +1056,7 @@ void ebpf_filesystem_read_hash(ebpf_module_t *em)
1056 *
1057 * Send hard disk information to Netdata.
1058 */
1043 -static void ebpf_histogram_send_data()
1059 +static void ebpf_histogram_send_data(void)
1060 {
1061 uint32_t i;
1062 uint32_t test = NETDATA_FILESYSTEM_FLAG_HAS_PARTITION | NETDATA_FILESYSTEM_REMOVE_CHARTS;
@@ -1092,9 +1108,14 @@ static void filesystem_collector(ebpf_module_t *em)
1108 heartbeat_t hb;
1109 heartbeat_init(&hb, USEC_PER_SEC);
1110 while (!ebpf_plugin_stop() && running_time < lifetime) {
1111 + if (ebpf_plugin_stop())
1112 + break;
1113 +
1114 heartbeat_next(&hb);
1115 + if (ebpf_plugin_stop())
1116 + break;
1117
1097 - if (ebpf_plugin_stop() || ++counter != update_every)
1118 + if (++counter != update_every)
1119 continue;
1120
1121 counter = 0;
@@ -1106,11 +1127,14 @@ static void filesystem_collector(ebpf_module_t *em)
1127
1128 netdata_mutex_unlock(&lock);
1129
1130 + if (ebpf_plugin_stop())
1131 + break;
1132 +
1133 netdata_mutex_lock(&ebpf_exit_cleanup);
1110 - if (running_time && !em->running_time)
1111 - running_time = update_every;
1112 - else
1134 + if (running_time)
1135 running_time += update_every;
1136 + else
1137 + running_time = update_every;
1138
1139 em->running_time = running_time;
1140 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -1128,15 +1152,14 @@ static void filesystem_collector(ebpf_module_t *em)
1152 *
1153 * Update file system structure using values read from configuration file.
1154 */
1131 -static void ebpf_update_filesystem()
1155 +static void ebpf_update_filesystem(void)
1156 {
1157 char dist[NETDATA_FS_MAX_DIST_NAME + 1];
1158 int i;
1159 for (i = 0; localfs[i].filesystem; i++) {
1160 snprintfz(dist, NETDATA_FS_MAX_DIST_NAME, "%sdist", localfs[i].filesystem);
1161
1138 - localfs[i].enabled = inicfg_get_boolean(&fs_config, NETDATA_FILESYSTEM_CONFIG_NAME, dist,
1139 - CONFIG_BOOLEAN_YES);
1162 + localfs[i].enabled = inicfg_get_boolean(&fs_config, NETDATA_FILESYSTEM_CONFIG_NAME, dist, CONFIG_BOOLEAN_YES);
1163 }
1164 }
1165
@@ -1146,7 +1169,7 @@ static void ebpf_update_filesystem()
1169 * When thread is initialized the variable fs_maps is set as null,
1170 * this function fills the variable before to use.
1171 */
1149 -static void ebpf_set_maps()
1172 +static void ebpf_set_maps(void)
1173 {
1174 localfs[NETDATA_FS_LOCALFS_EXT4].fs_maps = ext4_maps;
1175 localfs[NETDATA_FS_LOCALFS_XFS].fs_maps = xfs_maps;
@@ -1158,7 +1181,7 @@ static void ebpf_set_maps()
1181 /**
1182 * Filesystem thread
1183 *
1161 - * Thread used to generate socket charts.
1184 + * Thread used to generate filesystem charts.
1185 *
1186 * @param ptr a pointer to `struct ebpf_module`
1187 *
@@ -1170,6 +1193,10 @@ void ebpf_filesystem_thread(void *ptr)
1193
1194 CLEANUP_FUNCTION_REGISTER(ebpf_filesystem_exit) cleanup_ptr = em;
1195
1196 + if (!ebpf_module_thread_has_valid_state(em)) {
1197 + goto endfilesystem;
1198 + }
1199 +
1200 ebpf_set_maps();
1201 ebpf_update_filesystem();
1202
src/collectors/ebpf.plugin/ebpf_filesystem.h
+50 -9
@@ -7,20 +7,21 @@
7 #define NETDATA_EBPF_MODULE_NAME_FILESYSTEM "filesystem"
8 #define NETDATA_EBPF_FS_MODULE_DESC "Monitor filesystem latency for: btrfs, ext4, nfs, xfs and zfs."
9
10 -#include "ebpf.h"
10 +#include "libnetdata/libnetdata.h"
11
12 +// Forward declaration to avoid circular dependency
13 +struct ebpf_module;
14 +
15 +// Constants
16 #define NETDATA_FS_MAX_DIST_NAME 64UL
17 +#define NETDATA_FS_TEMP_MAP_SIZE 4192
18 +#define NETDATA_FS_HISTOGRAM_BINS 24
19 +#define NETDATA_PARTITION_UPDATE_INTERVAL_MULTIPLIER 5
20
21 +// Configuration section and file names
22 #define NETDATA_FILESYSTEM_CONFIG_NAME "filesystem"
15 -
16 -// Process configuration name
23 #define NETDATA_FILESYSTEM_CONFIG_FILE "filesystem.conf"
24
19 -typedef struct netdata_fs_hist {
20 - uint32_t hist_id;
21 - uint32_t bin;
22 -} netdata_fs_hist_t;
23 -
25 enum filesystem_limit {
26 NETDATA_KEY_CALLS_READ = 24,
27 NETDATA_KEY_CALLS_WRITE = 48,
@@ -29,7 +30,8 @@ enum filesystem_limit {
30 };
31
32 enum netdata_filesystem_flags {
32 - NETDATA_FILESYSTEM_FLAG_NO_PARTITION = 0,
33 + // Flags indicating filesystem module state and operations
34 + NETDATA_FILESYSTEM_FLAG_NO_PARTITION,
35 NETDATA_FILESYSTEM_LOAD_EBPF_PROGRAM = 1,
36 NETDATA_FILESYSTEM_FLAG_HAS_PARTITION = 2,
37 NETDATA_FILESYSTEM_FLAG_CHART_CREATED = 4,
@@ -50,7 +52,46 @@ enum netdata_filesystem_localfs_idx {
52 NETDATA_FS_LOCALFS_END,
53 };
54
55 +/**
56 + * Filesystem eBPF collector thread
57 + *
58 + * Main thread function that monitors filesystem operations (btrfs, ext4, nfs, xfs, zfs)
59 + * and collects latency metrics using eBPF.
60 + *
61 + * @param ptr Pointer to module data (struct ebpf_module *)
62 + */
63 void ebpf_filesystem_thread(void *ptr);
64 +
65 +/**
66 + * Initialize eBPF data
67 + *
68 + * @param em Main thread structure
69 + *
70 + * @return 0 on success, -1 on error
71 + */
72 +int ebpf_filesystem_initialize_ebpf_data(struct ebpf_module *em);
73 +
74 +/**
75 + * Cleanup eBPF data
76 + *
77 + * Frees allocated resources and cleans up filesystem partitions
78 + */
79 +void ebpf_filesystem_cleanup_ebpf_data(void);
80 +
81 +/**
82 + * Read filesystem hash
83 + *
84 + * Reads histogram data from filesystem eBPF maps
85 + *
86 + * @param em Pointer to module structure
87 + */
88 +void ebpf_filesystem_read_hash(struct ebpf_module *em);
89 +
90 +/**
91 + * Filesystem module configuration
92 + *
93 + * Stores configuration parameters for the filesystem eBPF collector
94 + */
95 extern struct config fs_config;
96
97 #endif /* NETDATA_EBPF_FILESYSTEM_H */
src/collectors/ebpf.plugin/ebpf_functions.c
+5
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_functions.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 /*****************************************************************
8 * EBPF FUNCTION COMMON
@@ -824,6 +825,10 @@ void ebpf_function_thread(void *ptr)
825 heartbeat_t hb;
826 heartbeat_init(&hb, USEC_PER_SEC);
827 while (!ebpf_plugin_stop()) {
828 + if (ebpf_plugin_stop()) {
829 + break;
830 + }
831 +
832 heartbeat_next(&hb);
833
834 if (ebpf_plugin_stop()) {
src/collectors/ebpf.plugin/ebpf_hardirq.c
+251 -159
@@ -2,9 +2,15 @@
2
3 #include "ebpf.h"
4 #include "ebpf_hardirq.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 struct config hardirq_config = APPCONFIG_INITIALIZER;
8
9 +static char *hardirq_counter_dimension_name[NETDATA_HARDIRQ_DIMENSION] = {"latency"};
10 +
11 +static netdata_syscall_stat_t hardirq_counter_aggregated_data[NETDATA_HARDIRQ_DIMENSION];
12 +static netdata_publish_syscall_t hardirq_counter_publish_aggregated[NETDATA_HARDIRQ_DIMENSION];
13 +
14 static ebpf_local_maps_t hardirq_maps[] = {
15 {.name = "tbl_hardirq",
16 .internal_input = NETDATA_HARDIRQ_MAX_IRQS,
@@ -24,7 +30,6 @@ static ebpf_local_maps_t hardirq_maps[] = {
30 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
31 #endif
32 },
27 - /* end */
33 {.name = NULL,
34 .internal_input = 0,
35 .user_input = 0,
@@ -62,7 +67,6 @@ static ebpf_tracepoint_t hardirq_tracepoints[] = {
67 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "irq_work_exit"},
68 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "x86_platform_ipi_entry"},
69 {.enabled = false, .class = HARDIRQ_TP_CLASS_IRQ_VECTORS, .event = "x86_platform_ipi_exit"},
65 - /* end */
70 {.enabled = false, .class = NULL, .event = NULL}};
71
72 static hardirq_static_val_t hardirq_static_vals[] = {
@@ -79,9 +83,7 @@ static hardirq_static_val_t hardirq_static_vals[] = {
83 {.idx = HARDIRQ_EBPF_STATIC_X86_PLATFORM_IPI, .name = "x86_platform_ipi", .latency = 0},
84 };
85
82 -// store for "published" data from the reader thread, which the collector
83 -// thread will write to netdata agent.
84 -static avl_tree_lock hardirq_pub;
86 +static bool hardirq_safe_clean = false;
87
88 #ifdef LIBBPF_MAJOR_VERSION
89 /**
@@ -101,6 +103,11 @@ static inline void ebpf_hardirq_set_hash_table(struct hardirq_bpf *obj)
103 * Load and Attach
104 *
105 * Load and attach bpf software.
106 + *
107 + * @param obj is the main structure for bpf objects.
108 + * @param em structure with configuration
109 + *
110 + * @return It returns 0 on success and -1 otherwise.
111 */
112 static inline int ebpf_hardirq_load_and_attach(struct hardirq_bpf *obj)
113 {
@@ -109,28 +116,24 @@ static inline int ebpf_hardirq_load_and_attach(struct hardirq_bpf *obj)
116 return -1;
117 }
118
112 - return hardirq_bpf__attach(obj);
119 + ret = hardirq_bpf__attach(obj);
120 + if (ret) {
121 + return -1;
122 + }
123 +
124 + ebpf_hardirq_set_hash_table(obj);
125 +
126 + return 0;
127 }
128 #endif
129
130 /*****************************************************************
131 *
118 - * ARAL SECTION
132 + * JudyL SECTION
133 *
134 *****************************************************************/
135
122 -// ARAL vectors used to speed up processing
123 -ARAL *ebpf_aral_hardirq = NULL;
124 -
125 -/**
126 - * eBPF hardirq Aral init
127 - *
128 - * Initiallize array allocator that will be used when integration with apps is enabled.
129 - */
130 -static inline void ebpf_hardirq_aral_init()
131 -{
132 - ebpf_aral_hardirq = ebpf_allocate_pid_aral(NETDATA_EBPF_HARDIRQ_ARAL_NAME, sizeof(hardirq_val_t));
133 -}
136 +static Pvoid_t ebpf_hardirq_JudyL = NULL;
137
138 /**
139 * eBPF hardirq get
@@ -139,21 +142,44 @@ static inline void ebpf_hardirq_aral_init()
142 *
143 * @return it returns the address on success.
144 */
142 -hardirq_val_t *ebpf_hardirq_get(void)
145 +hardirq_val_t *ebpf_hardirq_get(int irq)
146 {
144 - hardirq_val_t *target = aral_mallocz(ebpf_aral_hardirq);
145 - memset(target, 0, sizeof(hardirq_val_t));
147 + Pvoid_t *PValue = JudyLGet(ebpf_hardirq_JudyL, (Word_t)irq, PJE0);
148 + if (PValue && *PValue)
149 + return *PValue;
150 +
151 + JError_t J_Error;
152 + PValue = JudyLIns(&ebpf_hardirq_JudyL, (Word_t)irq, &J_Error);
153 + if (unlikely(PValue == PJERR)) {
154 + netdata_log_error(
155 + "Cannot insert IRQ %d to JudyL, JU_ERRNO_* == %u, ID == %d", irq, JU_ERRNO(&J_Error), JU_ERRID(&J_Error));
156 + return NULL;
157 + }
158 +
159 + if (unlikely(!PValue)) {
160 + netdata_log_error("JudyLIns returned NULL for IRQ %d", irq);
161 + return NULL;
162 + }
163 +
164 + hardirq_val_t *target = callocz(1, sizeof(hardirq_val_t));
165 + target->irq = irq;
166 + *PValue = target;
167 +
168 return target;
169 }
170
171 /**
172 * eBPF hardirq release
173 *
152 - * @param stat Release a target after usage.
174 + * @param irq IRQ number to release.
175 */
154 -void ebpf_hardirq_release(hardirq_val_t *stat)
176 +void ebpf_hardirq_release(int irq)
177 {
156 - aral_freez(ebpf_aral_hardirq, stat);
178 + Pvoid_t *PValue = JudyLGet(ebpf_hardirq_JudyL, (Word_t)irq, PJE0);
179 + if (PValue && *PValue) {
180 + freez(*PValue);
181 + JudyLDel(&ebpf_hardirq_JudyL, (Word_t)irq, PJE0);
182 + }
183 }
184
185 /*****************************************************************
@@ -191,36 +217,65 @@ static void ebpf_obsolete_hardirq_global(ebpf_module_t *em)
217 *
218 * @param ptr thread data.
219 */
194 -static void hardirq_exit(void *pptr)
220 +static void hardirq_cleanup(void *pptr)
221 {
222 + return;
223 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
224 if (!em)
225 return;
226
200 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
227 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
228 netdata_mutex_lock(&lock);
229
203 - ebpf_obsolete_hardirq_global(em);
230 + if (hardirq_safe_clean)
231 + ebpf_obsolete_hardirq_global(em);
232
233 netdata_mutex_unlock(&lock);
234 fflush(stdout);
235 }
236
209 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
237 + if (!hardirq_safe_clean) {
238 + netdata_mutex_lock(&ebpf_exit_cleanup);
239 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
240 + netdata_mutex_unlock(&ebpf_exit_cleanup);
241 + return;
242 + }
243
211 - if (em->objects) {
212 - ebpf_unload_legacy_code(em->objects, em->probe_links);
213 - em->objects = NULL;
214 - em->probe_links = NULL;
244 + if (!ebpf_plugin_stop()) {
245 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
246 + ebpf_unload_legacy_code(em->objects, em->probe_links);
247 + em->objects = NULL;
248 + em->probe_links = NULL;
249 + }
250 +#ifdef LIBBPF_MAJOR_VERSION
251 + else if (hardirq_bpf_obj) {
252 + //hardirq_bpf__destroy(hardirq_bpf_obj);
253 + hardirq_bpf_obj = NULL;
254 + }
255 +#endif
256 }
257
258 + /*
259 + if (unlikely(ebpf_hardirq_JudyL)) {
260 + Word_t index = 0;
261 + Pvoid_t *PValue;
262 + for (PValue = JudyLFirst(ebpf_hardirq_JudyL, &index, PJE0); PValue != NULL && PValue != PJERR;
263 + PValue = JudyLNext(ebpf_hardirq_JudyL, &index, PJE0)) {
264 + hardirq_val_t *v = *PValue;
265 + if (v)
266 + freez(v);
267 + }
268 + JudyLFreeArray(&ebpf_hardirq_JudyL, PJE0);
269 + ebpf_hardirq_JudyL = NULL;
270 + }
271 + */
272 +
273 for (int i = 0; hardirq_tracepoints[i].class != NULL; i++) {
274 ebpf_disable_tracepoint(&hardirq_tracepoints[i]);
275 }
276
277 netdata_mutex_lock(&ebpf_exit_cleanup);
278 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
223 - ebpf_update_stats(&plugin_statistics, em);
279 netdata_mutex_unlock(&ebpf_exit_cleanup);
280 }
281
@@ -228,32 +283,10 @@ static void hardirq_exit(void *pptr)
283 * MAIN LOOP
284 *****************************************************************/
285
231 -/**
232 - * Compare hard IRQ values.
233 - *
234 - * @param a `hardirq_val_t *`.
235 - * @param b `hardirq_val_t *`.
236 - *
237 - * @return 0 if a==b, 1 if a>b, -1 if a<b.
238 -*/
239 -static int hardirq_val_cmp(void *a, void *b)
240 -{
241 - hardirq_val_t *ptr1 = a;
242 - hardirq_val_t *ptr2 = b;
243 -
244 - if (ptr1->irq > ptr2->irq) {
245 - return 1;
246 - } else if (ptr1->irq < ptr2->irq) {
247 - return -1;
248 - } else {
249 - return 0;
250 - }
251 -}
252 -
286 /**
287 * Parse interrupts
288 *
256 - * Parse /proc/interrupts to get names used in metrics
289 + * Parse /proc/interrupts to get names used in metrics
290 *
291 * @param irq_name vector to store data.
292 * @param irq irq value
@@ -274,7 +307,7 @@ static int hardirq_parse_interrupts(char *irq_name, int irq)
307
308 ff = procfile_readall(ff);
309 if (unlikely(!ff))
277 - return -1; // we return 0, so that we will retry to open it next time
310 + return -1;
311
312 size_t words = procfile_linewords(ff, 0);
313 if (unlikely(cpus == -1)) {
@@ -306,7 +339,6 @@ static int hardirq_parse_interrupts(char *irq_name, int irq)
339
340 if (unlikely((uint32_t)(cpus + 2) < words)) {
341 const char *name = procfile_lineword(ff, l, words - 1);
309 - // On some motherboards IRQ can have the same name, so we append IRQ id to differentiate.
342 snprintfz(irq_name, NETDATA_HARDIRQ_NAME_LEN - 1, "%d_%s", irq, name);
343 }
344 }
@@ -315,133 +347,122 @@ static int hardirq_parse_interrupts(char *irq_name, int irq)
347 }
348
349 /**
318 - * Read Latency MAP
350 + * Read Latency Map
351 *
352 * Read data from kernel ring to user ring.
353 *
354 * @param mapfd hash map id.
355 *
356 * @return it returns 0 on success and -1 otherwise
325 - */
357 static int hardirq_read_latency_map(int mapfd)
358 {
328 - static hardirq_ebpf_static_val_t *hardirq_ebpf_vals = NULL;
329 - if (!hardirq_ebpf_vals)
330 - hardirq_ebpf_vals = callocz(ebpf_nprocs + 1, sizeof(hardirq_ebpf_static_val_t));
359 + static hardirq_ebpf_static_val_t *hardirq_ebpf_dynamic_vals = NULL;
360 + if (!hardirq_ebpf_dynamic_vals)
361 + hardirq_ebpf_dynamic_vals = callocz(ebpf_nprocs, sizeof(hardirq_ebpf_static_val_t));
362
363 hardirq_ebpf_key_t key = {};
364 hardirq_ebpf_key_t next_key = {};
334 - hardirq_val_t search_v = {};
335 - hardirq_val_t *v = NULL;
365
366 while (bpf_map_get_next_key(mapfd, &key, &next_key) == 0) {
338 - // get val for this key.
339 - int test = bpf_map_lookup_elem(mapfd, &key, hardirq_ebpf_vals);
367 + if (ebpf_plugin_stop())
368 + break;
369 +
370 + int test = bpf_map_lookup_elem(mapfd, &key, hardirq_ebpf_dynamic_vals);
371 if (unlikely(test < 0)) {
372 key = next_key;
373 continue;
374 }
375
345 - // is this IRQ saved yet?
346 - //
347 - // if not, make a new one, mark it as unsaved for now, and continue; we
348 - // will insert it at the end after all of its values are correctly set,
349 - // so that we can safely publish it to the collector within a single,
350 - // short locked operation.
351 - //
352 - // otherwise simply continue; we will only update the latency, which
353 - // can be republished safely without a lock.
354 - //
355 - // NOTE: lock isn't strictly necessary for this initial search, as only
356 - // this thread does writing, but the AVL is using a read-write lock so
357 - // there is no congestion.
358 - bool v_is_new = false;
359 - search_v.irq = key.irq;
360 - v = (hardirq_val_t *)avl_search_lock(&hardirq_pub, (avl_t *)&search_v);
361 - if (unlikely(v == NULL)) {
362 - // latency/name can only be added reliably at a later time.
363 - // when they're added, only then will we AVL insert.
364 - v = ebpf_hardirq_get();
365 - v->irq = key.irq;
366 - v->dim_exists = false;
367 -
368 - v_is_new = true;
376 + if (unlikely(key.irq < 0 || key.irq >= NETDATA_HARDIRQ_MAX_IRQS)) {
377 + key = next_key;
378 + continue;
379 }
380
371 - // note two things:
372 - // 1. we must add up latency value for this IRQ across all CPUs.
373 - // 2. the name is unfortunately *not* available on all CPU maps - only
374 - // a single map contains the name, so we must find it. we only need
375 - // to copy it though if the IRQ is new for us.
376 - uint64_t total_latency = 0;
377 - int i;
378 - for (i = 0; i < ebpf_nprocs; i++) {
379 - total_latency += hardirq_ebpf_vals[i].latency / 1000;
381 + hardirq_val_t *v = ebpf_hardirq_get(key.irq);
382 + if (unlikely(!v)) {
383 + key = next_key;
384 + continue;
385 }
386
382 - // can now safely publish latency for existing IRQs.
383 - v->latency = total_latency;
384 -
385 - // can now safely publish new IRQ.
386 - if (v_is_new) {
387 + if (!v->dim_exists) {
388 if (hardirq_parse_interrupts(v->name, v->irq)) {
388 - ebpf_hardirq_release(v);
389 - return -1;
389 + ebpf_hardirq_release(v->irq);
390 + key = next_key;
391 + continue;
392 }
393 + v->dim_exists = true;
394 + }
395
392 - avl_t *check = avl_insert_lock(&hardirq_pub, (avl_t *)v);
393 - if (check != (avl_t *)v) {
394 - netdata_log_error("Internal error, cannot insert the AVL tree.");
395 - }
396 + uint64_t latency = 0;
397 + int i;
398 + for (i = 0; i < ebpf_nprocs; i++) {
399 + latency += hardirq_ebpf_dynamic_vals[i].latency / 1000;
400 }
401 + v->latency = latency;
402
403 key = next_key;
404 }
405
406 return 0;
407 }
408 + */
409
410 +/**
411 + * Read Latency Static Map
412 + *
413 + * Read data from kernel ring to user ring.
414 + *
415 + * @param mapfd array map id.
416 + */
417 static void hardirq_read_latency_static_map(int mapfd)
418 {
406 - static hardirq_ebpf_static_val_t *hardirq_ebpf_static_vals = NULL;
407 - if (!hardirq_ebpf_static_vals)
408 - hardirq_ebpf_static_vals = callocz(ebpf_nprocs + 1, sizeof(hardirq_ebpf_static_val_t));
419 + static hardirq_ebpf_static_val_t *hardirq_per_cpu_vals = NULL;
420 + if (!hardirq_per_cpu_vals)
421 + hardirq_per_cpu_vals = callocz(ebpf_nprocs, sizeof(hardirq_ebpf_static_val_t));
422
423 + int end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
424 uint32_t i;
425 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
412 - uint32_t map_i = hardirq_static_vals[i].idx;
413 - int test = bpf_map_lookup_elem(mapfd, &map_i, hardirq_ebpf_static_vals);
426 + int test = bpf_map_lookup_elem(mapfd, &i, hardirq_per_cpu_vals);
427 if (unlikely(test < 0)) {
428 continue;
429 }
430
418 - uint64_t total_latency = 0;
431 + uint64_t latency = 0;
432 int cpu_i;
420 - int end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
433 for (cpu_i = 0; cpu_i < end; cpu_i++) {
422 - total_latency += hardirq_ebpf_static_vals[cpu_i].latency / 1000;
434 + latency += hardirq_per_cpu_vals[cpu_i].latency / 1000;
435 }
436
425 - hardirq_static_vals[i].latency = total_latency;
437 + hardirq_static_vals[i].latency = latency;
438 }
439 }
440
441 /**
442 * Read eBPF maps for hard IRQ.
443 *
432 - * @return When it is not possible to parse /proc, it returns -1, on success it returns 0;
444 + * @return When it is not possible to parse /proc, it returns -1, on success it returns 0.
445 */
434 -static int hardirq_reader()
446 +static int hardirq_reader(void)
447 {
448 + /*
449 if (hardirq_read_latency_map(hardirq_maps[HARDIRQ_MAP_LATENCY].map_fd))
450 return -1;
451 + */
452
453 hardirq_read_latency_static_map(hardirq_maps[HARDIRQ_MAP_LATENCY_STATIC].map_fd);
454
455 return 0;
456 }
457
444 -static void hardirq_create_charts(int update_every)
458 +/**
459 + * Create charts
460 + *
461 + * Call ebpf_create_chart to create the charts for the collector.
462 + *
463 + * @param update_every value to overwrite the update frequency set by the server.
464 + */
465 +static void ebpf_create_hardirq_charts(int update_every)
466 {
467 ebpf_create_chart(
468 NETDATA_EBPF_SYSTEM_GROUP,
@@ -452,16 +473,21 @@ static void hardirq_create_charts(int update_every)
473 NETDATA_EBPF_SYSTEM_HARDIRQ_LATENCY_CTX,
474 NETDATA_EBPF_CHART_TYPE_STACKED,
475 NETDATA_CHART_PRIO_HARDIRQ_LATENCY,
455 - NULL,
456 - NULL,
457 - 0,
476 + ebpf_create_global_dimension,
477 + hardirq_counter_publish_aggregated,
478 + 1,
479 update_every,
480 NETDATA_EBPF_MODULE_NAME_HARDIRQ);
481
482 fflush(stdout);
483 }
484
464 -static void hardirq_create_static_dims()
485 +/**
486 + * Create static dimensions
487 + *
488 + * Create dimensions for static IRQs.
489 + */
490 +static void hardirq_create_static_dims(void)
491 {
492 uint32_t i;
493 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
@@ -470,14 +496,17 @@ static void hardirq_create_static_dims()
496 }
497 }
498
473 -// callback for avl tree traversal on `hardirq_pub`.
474 -static int hardirq_write_dims(void *entry, void *data)
499 +/**
500 + * Write dimensions
501 + *
502 + * Traverse JudyL array to write dimensions.
503 + *
504 + * @return It returns 1 to continue the iteration.
505 + */
506 +static int hardirq_write_dims(Word_t index, hardirq_val_t *v)
507 {
476 - UNUSED(data);
508 + (void)index;
509
478 - hardirq_val_t *v = entry;
479 -
480 - // IRQs get dynamically added in, so add the dimension if we haven't yet.
510 if (!v->dim_exists) {
511 ebpf_write_global_dimension(v->name, v->name, ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
512 v->dim_exists = true;
@@ -488,7 +517,29 @@ static int hardirq_write_dims(void *entry, void *data)
517 return 1;
518 }
519
491 -static inline void hardirq_write_static_dims()
520 +/**
521 + * Write all dimensions
522 + *
523 + * Traverse JudyL array and call hardirq_write_dims for each entry.
524 + */
525 +static inline void hardirq_write_all_dims(void)
526 +{
527 + Word_t index = 0;
528 + Pvoid_t *PValue;
529 + for (PValue = JudyLFirst(ebpf_hardirq_JudyL, &index, PJE0); PValue != NULL && PValue != PJERR;
530 + PValue = JudyLNext(ebpf_hardirq_JudyL, &index, PJE0)) {
531 + hardirq_val_t *v = *PValue;
532 + if (v)
533 + hardirq_write_dims(index, v);
534 + }
535 +}
536 +
537 +/**
538 + * Write static dimensions
539 + *
540 + * Write dimensions for static IRQs.
541 + */
542 +static inline void hardirq_write_static_dims(void)
543 {
544 uint32_t i;
545 for (i = 0; i < HARDIRQ_EBPF_STATIC_END; i++) {
@@ -497,57 +548,60 @@ static inline void hardirq_write_static_dims()
548 }
549
550 /**
500 -* Main loop for this collector.
551 + * Main loop for this collector.
552 *
553 * @param em the main thread structure.
503 -*/
554 + */
555 static void hardirq_collector(ebpf_module_t *em)
556 {
506 - memset(&hardirq_pub, 0, sizeof(hardirq_pub));
507 - avl_init_lock(&hardirq_pub, hardirq_val_cmp);
508 - ebpf_hardirq_aral_init();
509 -
510 - // create chart and static dims.
557 netdata_mutex_lock(&lock);
512 - hardirq_create_charts(em->update_every);
558 + ebpf_create_hardirq_charts(em->update_every);
559 hardirq_create_static_dims();
560 ebpf_update_stats(&plugin_statistics, em);
561 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
562 netdata_mutex_unlock(&lock);
563
518 - // loop and read from published data until ebpf plugin is closed.
564 + heartbeat_t hb;
565 + heartbeat_init(&hb, USEC_PER_SEC);
566 int update_every = em->update_every;
567 int counter = update_every - 1;
521 - //This will be cancelled by its parent
568 uint32_t running_time = 0;
569 uint32_t lifetime = em->lifetime;
524 - heartbeat_t hb;
525 - heartbeat_init(&hb, USEC_PER_SEC);
570 while (!ebpf_plugin_stop() && running_time < lifetime) {
571 + if (ebpf_plugin_stop())
572 + break;
573 +
574 heartbeat_next(&hb);
575
529 - if (ebpf_plugin_stop() || ++counter != update_every)
576 + if (ebpf_plugin_stop())
577 + break;
578 +
579 + if (++counter != update_every)
580 continue;
581
582 counter = 0;
533 - if (hardirq_reader())
583 + if (hardirq_reader()) {
584 + hardirq_safe_clean = false;
585 break;
586 + }
587
588 netdata_mutex_lock(&lock);
589
538 - // write dims now for all hitherto discovered IRQs.
590 ebpf_write_begin_chart(NETDATA_EBPF_SYSTEM_GROUP, "hardirq_latency", "");
540 - avl_traverse_lock(&hardirq_pub, hardirq_write_dims, NULL);
591 + //hardirq_write_all_dims();
592 hardirq_write_static_dims();
593 ebpf_write_end_chart();
594
595 netdata_mutex_unlock(&lock);
596
597 + if (ebpf_plugin_stop())
598 + break;
599 +
600 netdata_mutex_lock(&ebpf_exit_cleanup);
547 - if (running_time && !em->running_time)
548 - running_time = update_every;
549 - else
601 + if (running_time)
602 running_time += update_every;
603 + else
604 + running_time = update_every;
605
606 em->running_time = running_time;
607 netdata_mutex_unlock(&ebpf_exit_cleanup);
@@ -558,7 +612,7 @@ static void hardirq_collector(ebpf_module_t *em)
612 * EBPF HARDIRQ THREAD
613 *****************************************************************/
614
561 -/*
615 +/**
616 * Load BPF
617 *
618 * Load BPF files.
@@ -570,6 +624,10 @@ static void hardirq_collector(ebpf_module_t *em)
624 static int ebpf_hardirq_load_bpf(ebpf_module_t *em)
625 {
626 int ret = 0;
627 +#ifdef LIBBPF_MAJOR_VERSION
628 + ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
629 +#endif
630 +
631 if (em->load & EBPF_LOAD_LEGACY) {
632 em->probe_links = ebpf_load_program(ebpf_plugin_dir, em, running_on_kernel, isrh, &em->objects);
633 if (!em->probe_links) {
@@ -583,15 +641,32 @@ static int ebpf_hardirq_load_bpf(ebpf_module_t *em)
641 ret = -1;
642 else {
643 ret = ebpf_hardirq_load_and_attach(hardirq_bpf_obj);
586 - if (!ret)
587 - ebpf_hardirq_set_hash_table(hardirq_bpf_obj);
644 + if (ret) {
645 + hardirq_bpf__destroy(hardirq_bpf_obj);
646 + hardirq_bpf_obj = NULL;
647 + }
648 }
649 }
650 #endif
651
652 + if (ret)
653 + netdata_log_error("%s %s", EBPF_DEFAULT_ERROR_MSG, em->info.thread_name);
654 +
655 return ret;
656 }
657
658 +/**
659 + * Allocate vectors used with this thread.
660 + *
661 + * We are not testing the return, because callocz does this and shutdown the software
662 + * case it was not possible to allocate.
663 + */
664 +static void ebpf_hardirq_allocate_global_vectors()
665 +{
666 + memset(hardirq_counter_aggregated_data, 0, NETDATA_HARDIRQ_DIMENSION * sizeof(netdata_syscall_stat_t));
667 + memset(hardirq_counter_publish_aggregated, 0, NETDATA_HARDIRQ_DIMENSION * sizeof(netdata_publish_syscall_t));
668 +}
669 +
670 /**
671 * Hard IRQ latency thread.
672 *
@@ -600,9 +675,14 @@ static int ebpf_hardirq_load_bpf(ebpf_module_t *em)
675 */
676 void ebpf_hardirq_thread(void *ptr)
677 {
678 + return;
679 ebpf_module_t *em = (ebpf_module_t *)ptr;
680
605 - CLEANUP_FUNCTION_REGISTER(hardirq_exit) cleanup_ptr = em;
681 + CLEANUP_FUNCTION_REGISTER(hardirq_cleanup) cleanup_ptr = em;
682 +
683 + if (!ebpf_module_thread_has_valid_state(em)) {
684 + goto endhardirq;
685 + }
686
687 em->maps = hardirq_maps;
688
@@ -611,13 +691,25 @@ void ebpf_hardirq_thread(void *ptr)
691 }
692
693 #ifdef LIBBPF_MAJOR_VERSION
614 - ebpf_define_map_type(em->maps, em->maps_per_core, running_on_kernel);
694 ebpf_adjust_thread_load(em, default_btf);
695 #endif
696 if (ebpf_hardirq_load_bpf(em)) {
697 goto endhardirq;
698 }
699
700 + ebpf_hardirq_allocate_global_vectors();
701 +
702 + int algorithms[NETDATA_HARDIRQ_DIMENSION] = {NETDATA_EBPF_INCREMENTAL_IDX};
703 +
704 + ebpf_global_labels(
705 + hardirq_counter_aggregated_data,
706 + hardirq_counter_publish_aggregated,
707 + hardirq_counter_dimension_name,
708 + hardirq_counter_dimension_name,
709 + algorithms,
710 + NETDATA_HARDIRQ_DIMENSION);
711 +
712 + hardirq_safe_clean = true;
713 hardirq_collector(em);
714
715 endhardirq:
src/collectors/ebpf.plugin/ebpf_hardirq.h
+7 -10
@@ -7,7 +7,8 @@
7 #define NETDATA_EBPF_HARDIRQ_MODULE_DESC "Show time spent servicing individual hardware interrupt requests (hard IRQs)."
8
9 #include <stdint.h>
10 -#include "libnetdata/avl/avl.h"
10 +#include <stdbool.h>
11 +#include "libnetdata/libnetdata.h"
12
13 /*****************************************************************
14 * copied from kernel-collectors repo, with modifications needed
@@ -41,26 +42,18 @@ enum hardirq_maps { HARDIRQ_MAP_LATENCY, HARDIRQ_MAP_LATENCY_STATIC };
42
43 typedef struct hardirq_ebpf_static_val {
44 uint64_t latency;
44 - uint64_t ts;
45 } hardirq_ebpf_static_val_t;
46
47 /*****************************************************************
48 * below this is eBPF plugin-specific code.
49 *****************************************************************/
50
51 -// ARAL Name
52 -#define NETDATA_EBPF_HARDIRQ_ARAL_NAME "ebpf_harddirq"
53 -
51 #define NETDATA_EBPF_MODULE_NAME_HARDIRQ "hardirq"
52 #define NETDATA_HARDIRQ_CONFIG_FILE "hardirq.conf"
53
54 typedef struct hardirq_val {
58 - // must be at top for simplified AVL tree usage.
59 - // if it's not at the top, we need to use `containerof` for almost all ops.
60 - avl_t avl;
61 -
55 int irq;
63 - bool dim_exists; // keep this after `int irq` for alignment byte savings.
56 + bool dim_exists;
57 uint64_t latency;
58 char name[NETDATA_HARDIRQ_NAME_LEN];
59 } hardirq_val_t;
@@ -72,8 +65,12 @@ typedef struct hardirq_static_val {
65 } hardirq_static_val_t;
66
67 #define NETDATA_EBPF_SYSTEM_HARDIRQ_LATENCY_CTX "system.hardirq_latency"
68 +#define NETDATA_HARDIRQ_DIMENSION 1
69
70 extern struct config hardirq_config;
71 void ebpf_hardirq_thread(void *ptr);
72
73 +hardirq_val_t *ebpf_hardirq_get(int irq);
74 +void ebpf_hardirq_release(int irq);
75 +
76 #endif /* NETDATA_EBPF_HARDIRQ_H */
src/collectors/ebpf.plugin/ebpf_mdflush.c
+52 -63
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_mdflush.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 struct config mdflush_config = APPCONFIG_INITIALIZER;
8
@@ -27,6 +28,8 @@ netdata_ebpf_targets_t mdflush_targets[] = {
28 {.name = "md_flush_request", .mode = EBPF_LOAD_TRAMPOLINE},
29 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
30
31 +static bool mdflush_safe_clean = false;
32 +
33 // store for "published" data from the reader thread, which the collector
34 // thread will write to netdata agent.
35 static avl_tree_lock mdflush_pub;
@@ -149,13 +152,21 @@ static void ebpf_obsolete_mdflush_global(ebpf_module_t *em)
152 *
153 * @param ptr thread data.
154 */
155 +
156 static void mdflush_exit(void *pptr)
157 {
158 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
159 if (!em)
160 return;
161
158 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
162 + if (!mdflush_safe_clean) {
163 + netdata_mutex_lock(&ebpf_exit_cleanup);
164 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
165 + netdata_mutex_unlock(&ebpf_exit_cleanup);
166 + return;
167 + }
168 +
169 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
170 netdata_mutex_lock(&lock);
171
172 ebpf_obsolete_mdflush_global(em);
@@ -164,17 +175,11 @@ static void mdflush_exit(void *pptr)
175 fflush(stdout);
176 }
177
167 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
168 -
169 - if (em->objects) {
170 - ebpf_unload_legacy_code(em->objects, em->probe_links);
171 - em->objects = NULL;
172 - em->probe_links = NULL;
173 - }
178 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
179 + em->functions.bpf_unload(em);
180
181 netdata_mutex_lock(&ebpf_exit_cleanup);
182 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
177 - ebpf_update_stats(&plugin_statistics, em);
183 netdata_mutex_unlock(&ebpf_exit_cleanup);
184 }
185
@@ -195,9 +200,9 @@ static int mdflush_val_cmp(void *a, void *b)
200 return 1;
201 } else if (ptr1->unit < ptr2->unit) {
202 return -1;
198 - } else {
199 - return 0;
203 }
204 +
205 + return 0;
206 }
207
208 /**
@@ -211,64 +216,41 @@ static void mdflush_read_count_map(int maps_per_core)
216 {
217 int mapfd = mdflush_maps[MDFLUSH_MAP_COUNT].map_fd;
218 mdflush_ebpf_key_t curr_key = (uint32_t)-1;
214 - mdflush_ebpf_key_t key = (uint32_t)-1;
215 - netdata_mdflush_t search_v;
216 - netdata_mdflush_t *v = NULL;
219 + mdflush_ebpf_key_t key;
220 +
221 + int end = maps_per_core ? ebpf_nprocs : 1;
222
223 while (bpf_map_get_next_key(mapfd, &curr_key, &key) == 0) {
224 + if (ebpf_plugin_stop())
225 + break;
226 +
227 curr_key = key;
228
221 - // get val for this key.
222 - int test = bpf_map_lookup_elem(mapfd, &key, mdflush_ebpf_vals);
223 - if (unlikely(test < 0)) {
229 + int ret = bpf_map_lookup_elem(mapfd, &key, mdflush_ebpf_vals);
230 + if (unlikely(ret < 0)) {
231 continue;
232 }
233
227 - // is this record saved yet?
228 - //
229 - // if not, make a new one, mark it as unsaved for now, and continue; we
230 - // will insert it at the end after all of its values are correctly set,
231 - // so that we can safely publish it to the collector within a single,
232 - // short locked operation.
233 - //
234 - // otherwise simply continue; we will only update the flush count,
235 - // which can be republished safely without a lock.
236 - //
237 - // NOTE: lock isn't strictly necessary for this initial search, as only
238 - // this thread does writing, but the AVL is using a read-write lock so
239 - // there is no congestion.
240 - bool v_is_new = false;
241 - search_v.unit = key;
242 - v = (netdata_mdflush_t *)avl_search_lock(&mdflush_pub, (avl_t *)&search_v);
234 + netdata_mdflush_t search_v = {.unit = key};
235 + netdata_mdflush_t *v = (netdata_mdflush_t *)avl_search_lock(&mdflush_pub, (avl_t *)&search_v);
236 if (unlikely(v == NULL)) {
244 - // flush count can only be added reliably at a later time.
245 - // when they're added, only then will we AVL insert.
237 v = callocz(1, sizeof(netdata_mdflush_t));
238 v->unit = key;
248 - sprintf(v->disk_name, "md%u", key);
239 + snprintf(v->disk_name, sizeof(v->disk_name), "md%u", key);
240 v->dim_exists = false;
241
251 - v_is_new = true;
242 + avl_t *check = avl_insert_lock(&mdflush_pub, (avl_t *)v);
243 + if (check != (avl_t *)v) {
244 + netdata_log_error("Internal error, cannot insert the AVL tree.");
245 + }
246 }
247
254 - // we must add up count value for this record across all CPUs.
248 uint64_t total_cnt = 0;
249 int i;
257 - int end = (!maps_per_core) ? 1 : ebpf_nprocs;
250 for (i = 0; i < end; i++) {
251 total_cnt += mdflush_ebpf_vals[i];
252 }
261 -
262 - // can now safely publish count for existing records.
253 v->cnt = total_cnt;
264 -
265 - // can now safely publish new record.
266 - if (v_is_new) {
267 - avl_t *check = avl_insert_lock(&mdflush_pub, (avl_t *)v);
268 - if (check != (avl_t *)v) {
269 - netdata_log_error("Internal error, cannot insert the AVL tree.");
270 - }
271 - }
254 }
255 }
256
@@ -293,13 +275,10 @@ static void mdflush_create_charts(int update_every)
275 }
276
277 // callback for avl tree traversal on `mdflush_pub`.
296 -static int mdflush_write_dims(void *entry, void *data)
278 +static int mdflush_write_dims(void *entry, void *data __maybe_unused)
279 {
298 - UNUSED(data);
299 -
280 netdata_mdflush_t *v = entry;
281
302 - // records get dynamically added in, so add the dim if we haven't yet.
282 if (!v->dim_exists) {
283 ebpf_write_global_dimension(v->disk_name, v->disk_name, ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
284 v->dim_exists = true;
@@ -327,21 +306,25 @@ static void mdflush_collector(ebpf_module_t *em)
306 ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_ADD);
307 netdata_mutex_unlock(&lock);
308
330 - // loop and read from published data until ebpf plugin is closed.
309 int counter = update_every - 1;
332 - int maps_per_core = em->maps_per_core;
310 uint32_t running_time = 0;
311 uint32_t lifetime = em->lifetime;
312 heartbeat_t hb;
313 heartbeat_init(&hb, USEC_PER_SEC);
314 while (!ebpf_plugin_stop() && running_time < lifetime) {
315 + if (ebpf_plugin_stop())
316 + break;
317 +
318 heartbeat_next(&hb);
319
340 - if (ebpf_plugin_stop() || ++counter != update_every)
320 + if (ebpf_plugin_stop())
321 + break;
322 +
323 + if (++counter != update_every)
324 continue;
325
326 counter = 0;
344 - mdflush_read_count_map(maps_per_core);
327 + mdflush_read_count_map(em->maps_per_core);
328 netdata_mutex_lock(&lock);
329 // write dims now for all hitherto discovered devices.
330 ebpf_write_begin_chart("mdstat", "mdstat_flush", "");
@@ -350,12 +333,11 @@ static void mdflush_collector(ebpf_module_t *em)
333
334 netdata_mutex_unlock(&lock);
335
353 - netdata_mutex_lock(&ebpf_exit_cleanup);
354 - if (running_time && !em->running_time)
355 - running_time = update_every;
356 - else
357 - running_time += update_every;
336 + if (ebpf_plugin_stop())
337 + break;
338
339 + netdata_mutex_lock(&ebpf_exit_cleanup);
340 + running_time += update_every;
341 em->running_time = running_time;
342 netdata_mutex_unlock(&ebpf_exit_cleanup);
343 }
@@ -414,9 +396,15 @@ void ebpf_mdflush_thread(void *ptr)
396 ebpf_module_t *em = (ebpf_module_t *)ptr;
397 CLEANUP_FUNCTION_REGISTER(mdflush_exit) cleanup_ptr = em;
398
399 + char *md_flush_request = NULL;
400 +
401 + if (!ebpf_module_thread_has_valid_state(em)) {
402 + goto endmdflush;
403 + }
404 +
405 em->maps = mdflush_maps;
406
419 - char *md_flush_request = ebpf_find_symbol("md_flush_request");
407 + md_flush_request = ebpf_find_symbol("md_flush_request");
408 if (!md_flush_request) {
409 netdata_log_error("Cannot monitor MD devices, because md is not loaded.");
410 goto endmdflush;
@@ -431,6 +419,7 @@ void ebpf_mdflush_thread(void *ptr)
419 goto endmdflush;
420 }
421
422 + mdflush_safe_clean = true;
423 mdflush_collector(em);
424
425 endmdflush:
src/collectors/ebpf.plugin/ebpf_mount.c
+48 -31
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_mount.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static ebpf_local_maps_t mount_maps[] = {
8 {.name = "tbl_mount",
@@ -178,16 +179,15 @@ static void ebpf_mount_set_hash_tables(struct mount_bpf *obj)
179 */
180 static inline int ebpf_mount_load_and_attach(struct mount_bpf *obj, ebpf_module_t *em)
181 {
181 - netdata_ebpf_targets_t *mt = em->targets;
182 - netdata_ebpf_program_loaded_t test = mt[NETDATA_MOUNT_SYSCALL].mode;
182 + netdata_ebpf_program_loaded_t mode = em->targets[NETDATA_MOUNT_SYSCALL].mode;
183
184 // We are testing only one, because all will have the same behavior
185 - if (test == EBPF_LOAD_TRAMPOLINE) {
185 + if (mode == EBPF_LOAD_TRAMPOLINE) {
186 ebpf_mount_disable_probe(obj);
187 ebpf_mount_disable_tracepoint(obj);
188
189 netdata_set_trampoline_target(obj);
190 - } else if (test == EBPF_LOAD_PROBE || test == EBPF_LOAD_RETPROBE) {
190 + } else if (mode == EBPF_LOAD_PROBE || mode == EBPF_LOAD_RETPROBE) {
191 ebpf_mount_disable_tracepoint(obj);
192 ebpf_mount_disable_trampoline(obj);
193 } else {
@@ -199,7 +199,7 @@ static inline int ebpf_mount_load_and_attach(struct mount_bpf *obj, ebpf_module_
199
200 int ret = mount_bpf__load(obj);
201 if (!ret) {
202 - if (test != EBPF_LOAD_PROBE && test != EBPF_LOAD_RETPROBE)
202 + if (mode != EBPF_LOAD_PROBE && mode != EBPF_LOAD_RETPROBE)
203 ret = mount_bpf__attach(obj);
204 else
205 ret = ebpf_mount_attach_probe(obj);
@@ -258,13 +258,28 @@ static void ebpf_obsolete_mount_global(ebpf_module_t *em)
258 *
259 * @param ptr thread data.
260 */
261 +void ebpf_mount_unload_bpf(ebpf_module_t *em)
262 +{
263 +#ifdef LIBBPF_MAJOR_VERSION
264 + if (mount_bpf_obj) {
265 + mount_bpf__destroy(mount_bpf_obj);
266 + mount_bpf_obj = NULL;
267 + }
268 +#endif
269 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
270 + ebpf_unload_legacy_code(em->objects, em->probe_links);
271 + em->objects = NULL;
272 + em->probe_links = NULL;
273 + }
274 +}
275 +
276 static void ebpf_mount_exit(void *pptr)
277 {
278 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
279 if (!em)
280 return;
281
267 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
282 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
283 netdata_mutex_lock(&lock);
284
285 ebpf_obsolete_mount_global(em);
@@ -273,23 +288,11 @@ static void ebpf_mount_exit(void *pptr)
288 netdata_mutex_unlock(&lock);
289 }
290
276 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
277 -
278 -#ifdef LIBBPF_MAJOR_VERSION
279 - if (mount_bpf_obj) {
280 - mount_bpf__destroy(mount_bpf_obj);
281 - mount_bpf_obj = NULL;
282 - }
283 -#endif
284 - if (em->objects) {
285 - ebpf_unload_legacy_code(em->objects, em->probe_links);
286 - em->objects = NULL;
287 - em->probe_links = NULL;
288 - }
291 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
292 + em->functions.bpf_unload(em);
293
294 netdata_mutex_lock(&ebpf_exit_cleanup);
295 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
292 - ebpf_update_stats(&plugin_statistics, em);
296 netdata_mutex_unlock(&ebpf_exit_cleanup);
297 }
298
@@ -322,7 +325,7 @@ static void ebpf_mount_read_global_table(int maps_per_core)
325 int fd = mount_maps[NETDATA_KEY_MOUNT_TABLE].map_fd;
326
327 for (idx = NETDATA_KEY_MOUNT_CALL; idx < NETDATA_MOUNT_END; idx++) {
325 - if (!bpf_map_lookup_elem(fd, &idx, stored)) {
328 + if (bpf_map_lookup_elem(fd, &idx, stored) == 0) {
329 int i;
330 int end = (maps_per_core) ? ebpf_nprocs : 1;
331 netdata_idx_t total = 0;
@@ -340,11 +343,11 @@ static void ebpf_mount_read_global_table(int maps_per_core)
343 */
344 static void ebpf_mount_send_data()
345 {
343 - int i, j;
346 + int i;
347 int end = NETDATA_EBPF_MOUNT_SYSCALL;
345 - for (i = NETDATA_KEY_MOUNT_CALL, j = NETDATA_KEY_MOUNT_ERROR; i < end; i++, j++) {
348 + for (i = 0; i < end; i++) {
349 mount_publish_aggregated[i].ncall = mount_hash_values[i];
347 - mount_publish_aggregated[i].nerr = mount_hash_values[j];
350 + mount_publish_aggregated[i].nerr = mount_hash_values[i + NETDATA_EBPF_MOUNT_SYSCALL];
351 }
352
353 write_count_chart(
@@ -375,8 +378,14 @@ static void mount_collector(ebpf_module_t *em)
378 heartbeat_t hb;
379 heartbeat_init(&hb, USEC_PER_SEC);
380 while (!ebpf_plugin_stop() && running_time < lifetime) {
381 + if (ebpf_plugin_stop())
382 + break;
383 +
384 heartbeat_next(&hb);
379 - if (ebpf_plugin_stop() || ++counter != update_every)
385 + if (ebpf_plugin_stop())
386 + break;
387 +
388 + if (++counter != update_every)
389 continue;
390
391 counter = 0;
@@ -387,12 +396,11 @@ static void mount_collector(ebpf_module_t *em)
396
397 netdata_mutex_unlock(&lock);
398
390 - netdata_mutex_lock(&ebpf_exit_cleanup);
391 - if (running_time && !em->running_time)
392 - running_time = update_every;
393 - else
394 - running_time += update_every;
399 + if (ebpf_plugin_stop())
400 + break;
401
402 + netdata_mutex_lock(&ebpf_exit_cleanup);
403 + running_time += update_every;
404 em->running_time = running_time;
405 netdata_mutex_unlock(&ebpf_exit_cleanup);
406 }
@@ -477,8 +485,13 @@ static int ebpf_mount_load_bpf(ebpf_module_t *em)
485 mount_bpf_obj = mount_bpf__open();
486 if (!mount_bpf_obj)
487 ret = -1;
480 - else
488 + else {
489 ret = ebpf_mount_load_and_attach(mount_bpf_obj, em);
490 + if (ret) {
491 + mount_bpf__destroy(mount_bpf_obj);
492 + mount_bpf_obj = NULL;
493 + }
494 + }
495 }
496 #endif
497
@@ -502,6 +515,10 @@ void ebpf_mount_thread(void *ptr)
515 ebpf_module_t *em = ptr;
516 CLEANUP_FUNCTION_REGISTER(ebpf_mount_exit) cleanup_ptr = em;
517
518 + if (!ebpf_module_thread_has_valid_state(em)) {
519 + goto endmount;
520 + }
521 +
522 em->maps = mount_maps;
523
524 #ifdef LIBBPF_MAJOR_VERSION
src/collectors/ebpf.plugin/ebpf_mount.h
+2 -7
@@ -25,14 +25,9 @@ enum mount_counters {
25 NETDATA_MOUNT_END
26 };
27
28 -enum mount_tables { NETDATA_KEY_MOUNT_TABLE };
28 +#define NETDATA_KEY_MOUNT_TABLE 0
29
30 -enum netdata_mount_syscalls {
31 - NETDATA_MOUNT_SYSCALL,
32 - NETDATA_UMOUNT_SYSCALL,
33 -
34 - NETDATA_MOUNT_SYSCALLS_END
35 -};
30 +enum netdata_mount_syscalls { NETDATA_MOUNT_SYSCALL, NETDATA_UMOUNT_SYSCALL };
31
32 extern struct config mount_config;
33 void ebpf_mount_thread(void *ptr);
src/collectors/ebpf.plugin/ebpf_oomkill.c
+29 -32
@@ -2,10 +2,10 @@
2
3 #include "ebpf.h"
4 #include "ebpf_oomkill.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 struct config oomkill_config = APPCONFIG_INITIALIZER;
8
8 -#define OOMKILL_MAP_KILLCNT 0
9 static ebpf_local_maps_t oomkill_maps[] = {
10 {.name = "tbl_oomkill",
11 .internal_input = NETDATA_OOMKILL_MAX_ENTRIES,
@@ -116,11 +116,6 @@ static void ebpf_obsolete_oomkill_apps(ebpf_module_t *em)
116 netdata_mutex_unlock(&collect_data_mutex);
117 }
118
119 -/**
120 - * Clean up the main thread.
121 - *
122 - * @param ptr thread data.
123 - */
119 static void oomkill_cleanup(void *pptr)
120 {
121 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
@@ -131,7 +126,7 @@ static void oomkill_cleanup(void *pptr)
126 collect_pids &= ~(1 << EBPF_MODULE_OOMKILL_IDX);
127 netdata_mutex_unlock(&lock);
128
134 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
129 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
130 netdata_mutex_lock(&lock);
131
132 if (em->cgroup_charts) {
@@ -144,17 +139,11 @@ static void oomkill_cleanup(void *pptr)
139 netdata_mutex_unlock(&lock);
140 }
141
147 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
148 -
149 - if (em->objects) {
150 - ebpf_unload_legacy_code(em->objects, em->probe_links);
151 - em->objects = NULL;
152 - em->probe_links = NULL;
153 - }
142 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
143 + em->functions.bpf_unload(em);
144
145 netdata_mutex_lock(&ebpf_exit_cleanup);
146 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
157 - ebpf_update_stats(&plugin_statistics, em);
147 netdata_mutex_unlock(&ebpf_exit_cleanup);
148 }
149
@@ -165,6 +154,9 @@ static void oomkill_write_data(int32_t *keys, uint32_t total)
154 uint32_t used_pid = 0;
155 netdata_mutex_lock(&collect_data_mutex);
156 for (w = apps_groups_root_target; w != NULL; w = w->next) {
157 + if (ebpf_plugin_stop())
158 + break;
159 +
160 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_OOMKILL_IDX))))
161 continue;
162
@@ -381,24 +373,21 @@ static uint32_t oomkill_read_data(int32_t *keys)
373 uint32_t i = 0;
374
375 uint32_t curr_key = 0;
384 - uint32_t key = 0;
385 - int mapfd = oomkill_maps[OOMKILL_MAP_KILLCNT].map_fd;
386 - uint32_t limit = NETDATA_OOMKILL_MAX_ENTRIES - 1;
376 + uint32_t key;
377 + int mapfd = oomkill_maps[0].map_fd;
378 while (bpf_map_get_next_key(mapfd, &curr_key, &key) == 0) {
379 + if (ebpf_plugin_stop())
380 + break;
381 +
382 curr_key = key;
383
384 keys[i] = (int32_t)key;
385 i += 1;
386
393 - // delete this key now that we've recorded its existence. there's no
394 - // race here, as the same PID will only get OOM killed once.
395 - int test = bpf_map_delete_elem(mapfd, &key);
396 - if (unlikely(test < 0)) {
397 - // since there's only 1 thread doing these deletions, it should be
398 - // impossible to get this condition.
387 + if (unlikely(bpf_map_delete_elem(mapfd, &key) < 0)) {
388 netdata_log_error("key unexpectedly not available for deletion.");
389 }
401 - if (i > limit)
390 + if (i >= NETDATA_OOMKILL_MAX_ENTRIES)
391 break;
392 }
393
@@ -447,11 +436,7 @@ static void ebpf_update_oomkill_cgroup(int32_t *keys, uint32_t total)
436 static int ebpf_update_oomkill_period(int running_time, ebpf_module_t *em)
437 {
438 netdata_mutex_lock(&ebpf_exit_cleanup);
450 - if (running_time && !em->running_time)
451 - running_time = em->update_every;
452 - else
453 - running_time += em->update_every;
454 -
439 + running_time += em->update_every;
440 em->running_time = running_time;
441 netdata_mutex_unlock(&ebpf_exit_cleanup);
442
@@ -468,7 +453,6 @@ static void oomkill_collector(ebpf_module_t *em)
453 int cgroups = em->cgroup_charts;
454 int update_every = em->update_every;
455 int32_t keys[NETDATA_OOMKILL_MAX_ENTRIES];
471 - memset(keys, 0, sizeof(keys));
456
457 // loop and read until ebpf plugin is closed.
458 int counter = update_every - 1;
@@ -478,8 +462,14 @@ static void oomkill_collector(ebpf_module_t *em)
462 heartbeat_t hb;
463 heartbeat_init(&hb, USEC_PER_SEC);
464 while (!ebpf_plugin_stop() && running_time < lifetime) {
465 + if (ebpf_plugin_stop())
466 + break;
467 +
468 (void)heartbeat_next(&hb);
482 - if (ebpf_plugin_stop() || ++counter != update_every)
469 + if (ebpf_plugin_stop())
470 + break;
471 +
472 + if (++counter != update_every)
473 continue;
474
475 counter = 0;
@@ -492,6 +482,9 @@ static void oomkill_collector(ebpf_module_t *em)
482 if (cgroups && shm_ebpf_cgroup.header)
483 ebpf_update_oomkill_cgroup(keys, count);
484
485 + if (ebpf_plugin_stop())
486 + break;
487 +
488 netdata_apps_integration_flags_t apps = em->apps_charts;
489 netdata_mutex_lock(&lock);
490 // write everything from the ebpf map.
@@ -561,6 +554,10 @@ void ebpf_oomkill_thread(void *ptr)
554
555 CLEANUP_FUNCTION_REGISTER(oomkill_cleanup) cleanup_ptr = em;
556
557 + if (!ebpf_module_thread_has_valid_state(em)) {
558 + goto endoomkill;
559 + }
560 +
561 em->maps = oomkill_maps;
562
563 #define NETDATA_DEFAULT_OOM_DISABLED_MSG "Disabling OOMKILL thread, because"
src/collectors/ebpf.plugin/ebpf_process.c
+150 -86
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_process.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 /*****************************************************************
8 *
@@ -14,11 +15,11 @@ static char *process_id_names[NETDATA_KEY_PUBLISH_PROCESS_END] = {"do_exit", "re
15 static char *status[] = {"process", "zombie"};
16
17 netdata_ebpf_targets_t process_targets[] = {
17 - {.name = "release_task", .mode = EBPF_LOAD_TRAMPOLINE},
18 - {.name = "__x64_sys_clone", .mode = EBPF_LOAD_TRAMPOLINE},
19 - {.name = "__x64_sys_clone3", .mode = EBPF_LOAD_TRAMPOLINE},
20 - {.name = "_do_fork", .mode = EBPF_LOAD_TRAMPOLINE},
21 - {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
18 + {.name = "release_task", .mode = EBPF_LOAD_TRAMPOLINE},
19 + {.name = "__x64_sys_clone", .mode = EBPF_LOAD_TRAMPOLINE},
20 + {.name = "__x64_sys_clone3", .mode = EBPF_LOAD_TRAMPOLINE},
21 + {.name = "_do_fork", .mode = EBPF_LOAD_TRAMPOLINE},
22 + {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE}};
23
24 static ebpf_local_maps_t process_maps[] = {
25 {.name = "tbl_pid_stats",
@@ -58,16 +59,17 @@ static ebpf_local_maps_t process_maps[] = {
59 #endif
60 }};
61
61 -char *tracepoint_sched_type = {"sched"};
62 -char *tracepoint_sched_process_exit = {"sched_process_exit"};
63 -char *tracepoint_sched_process_exec = {"sched_process_exec"};
64 -char *tracepoint_sched_process_fork = {"sched_process_fork"};
62 +char *tracepoint_sched_type = "sched";
63 +char *tracepoint_sched_process_exit = "sched_process_exit";
64 +char *tracepoint_sched_process_exec = "sched_process_exec";
65 +char *tracepoint_sched_process_fork = "sched_process_fork";
66 static int was_sched_process_exit_enabled = 0;
67 static int was_sched_process_exec_enabled = 0;
68 static int was_sched_process_fork_enabled = 0;
69
70 static netdata_idx_t *process_hash_values = NULL;
71 ebpf_process_stat_t *process_stat_vector = NULL;
72 +static bool process_safe_clean = false;
73 static netdata_syscall_stat_t process_aggregated_data[NETDATA_KEY_PUBLISH_PROCESS_END];
74 static netdata_publish_syscall_t process_publish_aggregated[NETDATA_KEY_PUBLISH_PROCESS_END];
75
@@ -98,14 +100,12 @@ static void ebpf_disable_tracepoints(struct process_bpf *obj)
100
101 static void ebpf_set_trampoline_target(struct process_bpf *obj)
102 {
101 - bpf_program__set_attach_target(obj->progs.netdata_release_task_fentry, 0,
102 - process_targets[PROCESS_RELEASE_TASK_NAME].name);
103 + bpf_program__set_attach_target(
104 + obj->progs.netdata_release_task_fentry, 0, process_targets[PROCESS_RELEASE_TASK_NAME].name);
105
104 - bpf_program__set_attach_target(obj->progs.netdata_clone_fexit, 0,
105 - process_targets[PROCESS_SYS_CLONE].name);
106 + bpf_program__set_attach_target(obj->progs.netdata_clone_fexit, 0, process_targets[PROCESS_SYS_CLONE].name);
107
107 - bpf_program__set_attach_target(obj->progs.netdata_clone3_fexit, 0,
108 - process_targets[PROCESS_SYS_CLONE3].name);
108 + bpf_program__set_attach_target(obj->progs.netdata_clone3_fexit, 0, process_targets[PROCESS_SYS_CLONE3].name);
109 }
110
111 /*
@@ -150,19 +150,19 @@ static inline void ebpf_adjust_process_fork(struct process_bpf *obj)
150 */
151 static inline int process_attach_kprobe_target(struct process_bpf *obj)
152 {
153 - obj->links.netdata_release_task_probe = bpf_program__attach_kprobe(obj->progs.netdata_release_task_probe,
154 - false, process_targets[PROCESS_RELEASE_TASK_NAME].name);
153 + obj->links.netdata_release_task_probe = bpf_program__attach_kprobe(
154 + obj->progs.netdata_release_task_probe, false, process_targets[PROCESS_RELEASE_TASK_NAME].name);
155 int ret = libbpf_get_error(obj->links.netdata_release_task_probe);
156 if (ret)
157 goto endakt;
158
159 if (running_on_kernel < NETDATA_EBPF_KERNEL_5_9_16) {
160 - obj->links.netdata_do_fork_probe = bpf_program__attach_kprobe(obj->progs.netdata_do_fork_probe,
161 - false, process_targets[PROCESS_SYS_FORK].name);
160 + obj->links.netdata_do_fork_probe =
161 + bpf_program__attach_kprobe(obj->progs.netdata_do_fork_probe, false, process_targets[PROCESS_SYS_FORK].name);
162 ret = libbpf_get_error(obj->links.netdata_do_fork_probe);
163 } else {
164 - obj->links.netdata_kernel_clone_probe = bpf_program__attach_kprobe(obj->progs.netdata_kernel_clone_probe,
165 - false, process_targets[PROCESS_KERNEL_CLONE].name);
164 + obj->links.netdata_kernel_clone_probe = bpf_program__attach_kprobe(
165 + obj->progs.netdata_kernel_clone_probe, false, process_targets[PROCESS_KERNEL_CLONE].name);
166 ret = libbpf_get_error(obj->links.netdata_kernel_clone_probe);
167 }
168 endakt:
@@ -195,21 +195,20 @@ static void ebpf_process_set_hash_tables(struct process_bpf *obj)
195 */
196 static inline int ebpf_process_load_and_attach(struct process_bpf *obj, ebpf_module_t *em)
197 {
198 - netdata_ebpf_targets_t *mt = em->targets;
199 - netdata_ebpf_program_loaded_t test = mt[PROCESS_RELEASE_TASK_NAME].mode;
200 - if (test == EBPF_LOAD_TRAMPOLINE) {
198 + netdata_ebpf_program_loaded_t mode = em->targets[PROCESS_RELEASE_TASK_NAME].mode;
199 + if (mode == EBPF_LOAD_TRAMPOLINE) {
200 ebpf_process_disable_probe(obj);
201 ebpf_disable_tracepoints(obj);
202
203 ebpf_set_trampoline_target(obj);
205 - } else if (test == EBPF_LOAD_PROBE || test == EBPF_LOAD_RETPROBE) {
204 + } else if (mode == EBPF_LOAD_PROBE || mode == EBPF_LOAD_RETPROBE) {
205 ebpf_disable_tracepoints(obj);
206 ebpf_disable_trampoline(obj);
207
209 - bpf_program__set_autoload((running_on_kernel <= NETDATA_EBPF_KERNEL_5_9_16) ?
210 - obj->progs.netdata_kernel_clone_probe :
211 - obj->progs.netdata_do_fork_probe,
212 - false);
208 + bpf_program__set_autoload(
209 + (running_on_kernel <= NETDATA_EBPF_KERNEL_5_9_16) ? obj->progs.netdata_kernel_clone_probe :
210 + obj->progs.netdata_do_fork_probe,
211 + false);
212 } else { // Tracepoint
213 ebpf_process_disable_probe(obj);
214 ebpf_disable_trampoline(obj);
@@ -226,7 +225,7 @@ static inline int ebpf_process_load_and_attach(struct process_bpf *obj, ebpf_mod
225 return ret;
226 }
227
229 - ret = (test == EBPF_LOAD_TRAMPOLINE) ? process_bpf__attach(obj) : process_attach_kprobe_target(obj);
228 + ret = (mode == EBPF_LOAD_TRAMPOLINE) ? process_bpf__attach(obj) : process_attach_kprobe_target(obj);
229 if (!ret) {
230 ebpf_process_set_hash_tables(obj);
231
@@ -263,8 +262,13 @@ static int ebpf_process_load_bpf(ebpf_module_t *em)
262 process_bpf_obj = process_bpf__open();
263 if (!process_bpf_obj)
264 ret = -1;
266 - else
265 + else {
266 ret = ebpf_process_load_and_attach(process_bpf_obj, em);
267 + if (ret) {
268 + process_bpf__destroy(process_bpf_obj);
269 + process_bpf_obj = NULL;
270 + }
271 + }
272 }
273 #endif
274
@@ -372,6 +376,9 @@ void ebpf_process_send_apps_data(struct ebpf_target *root, ebpf_module_t *em)
376 struct ebpf_target *w;
377
378 for (w = root; w; w = w->next) {
379 + if (ebpf_plugin_stop())
380 + break;
381 +
382 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_PROCESS_IDX))))
383 continue;
384
@@ -450,6 +457,9 @@ static void ebpf_update_process_cgroup()
457 ebpf_cgroup_target_t *ect;
458 netdata_mutex_lock(&mutex_cgroup_shm);
459 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
460 + if (ebpf_plugin_stop())
461 + break;
462 +
463 struct pid_on_target2 *pids;
464 for (pids = ect->pids; pids; pids = pids->next) {
465 uint32_t pid = pids->pid;
@@ -581,6 +591,9 @@ void ebpf_process_create_apps_charts(struct ebpf_module *em, void *ptr)
591 struct ebpf_target *w;
592 int update_every = em->update_every;
593 for (w = root; w; w = w->next) {
594 + if (ebpf_plugin_stop())
595 + break;
596 +
597 if (unlikely(!w->exposed))
598 continue;
599
@@ -923,7 +936,7 @@ static void ebpf_obsolete_process_global(ebpf_module_t *em)
936 */
937 static void ebpf_process_disable_tracepoints()
938 {
926 - char *default_message = {"Cannot disable the tracepoint"};
939 + char *default_message = "Cannot disable the tracepoint";
940 if (!was_sched_process_exit_enabled) {
941 if (ebpf_disable_tracing_values(tracepoint_sched_type, tracepoint_sched_process_exit))
942 netdata_log_error("%s %s/%s.", default_message, tracepoint_sched_type, tracepoint_sched_process_exit);
@@ -940,25 +953,25 @@ static void ebpf_process_disable_tracepoints()
953 }
954 }
955
943 -/**
944 - * Process Exit
945 - *
946 - * Cancel child thread.
947 - *
948 - * @param ptr thread data.
949 - */
956 static void ebpf_process_exit(void *pptr)
957 {
952 - pids_fd[NETDATA_EBPF_PIDS_PROCESS_IDX] = -1;
958 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_PROCESS_IDX, -1);
959 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
960 if (!em)
961 return;
962
963 + if (!process_safe_clean) {
964 + netdata_mutex_lock(&ebpf_exit_cleanup);
965 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
966 + netdata_mutex_unlock(&ebpf_exit_cleanup);
967 + return;
968 + }
969 +
970 netdata_mutex_lock(&lock);
971 collect_pids &= ~(1 << EBPF_MODULE_PROCESS_IDX);
972 netdata_mutex_unlock(&lock);
973
961 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
974 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
975 netdata_mutex_lock(&lock);
976 if (em->cgroup_charts) {
977 ebpf_obsolete_process_cgroup_charts(em);
@@ -975,23 +988,17 @@ static void ebpf_process_exit(void *pptr)
988 netdata_mutex_unlock(&lock);
989 }
990
978 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
979 -
980 - if (em->objects) {
981 - ebpf_unload_legacy_code(em->objects, em->probe_links);
982 - em->objects = NULL;
983 - em->probe_links = NULL;
984 - }
985 -
991 freez(process_hash_values);
992 freez(process_stat_vector);
993
994 ebpf_process_disable_tracepoints();
995
996 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
997 + em->functions.bpf_unload(em);
998 +
999 netdata_mutex_lock(&ebpf_exit_cleanup);
1000 process_pid_fd = -1;
1001 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
994 - ebpf_update_stats(&plugin_statistics, em);
1002 netdata_mutex_unlock(&ebpf_exit_cleanup);
1003 }
1004
@@ -1329,6 +1336,9 @@ static void ebpf_create_systemd_process_charts(ebpf_module_t *em)
1336 task_error.update_every = em->update_every;
1337
1338 for (w = ebpf_cgroup_pids; w; w = w->next) {
1339 + if (ebpf_plugin_stop())
1340 + break;
1341 +
1342 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_PROCESS_CHART))
1343 continue;
1344
@@ -1358,6 +1368,9 @@ static void ebpf_send_systemd_process_charts(ebpf_module_t *em)
1368 {
1369 ebpf_cgroup_target_t *ect;
1370 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1371 + if (ebpf_plugin_stop())
1372 + break;
1373 +
1374 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_PROCESS_CHART))) {
1375 continue;
1376 }
@@ -1399,6 +1412,11 @@ static void ebpf_process_send_cgroup_data(ebpf_module_t *em)
1412 ebpf_process_sum_cgroup_pids(&ect->publish_systemd_ps, ect->pids);
1413 }
1414
1415 + if (ebpf_plugin_stop()) {
1416 + netdata_mutex_unlock(&mutex_cgroup_shm);
1417 + return;
1418 + }
1419 +
1420 if (shm_ebpf_cgroup.header->systemd_enabled) {
1421 if (send_cgroup_chart) {
1422 ebpf_create_systemd_process_charts(em);
@@ -1408,6 +1426,9 @@ static void ebpf_process_send_cgroup_data(ebpf_module_t *em)
1426 }
1427
1428 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1429 + if (ebpf_plugin_stop())
1430 + break;
1431 +
1432 if (ect->systemd)
1433 continue;
1434
@@ -1457,6 +1478,9 @@ void ebpf_process_apps_accumulator(ebpf_process_stat_t *out, int maps_per_core)
1478 ebpf_process_stat_t *total = &out[0];
1479 uint64_t ct = total->ct;
1480 for (i = 1; i < end; i++) {
1481 + if (ebpf_plugin_stop())
1482 + break;
1483 +
1484 ebpf_process_stat_t *w = &out[i];
1485 total->exit_call += w->exit_call;
1486 total->task_err += w->task_err;
@@ -1480,6 +1504,9 @@ void ebpf_process_sum_values_for_pids(ebpf_process_stat_t *process, struct ebpf_
1504 {
1505 memset(process, 0, sizeof(ebpf_process_stat_t));
1506 for (; root; root = root->next) {
1507 + if (ebpf_plugin_stop())
1508 + break;
1509 +
1510 uint32_t pid = root->pid;
1511 netdata_ebpf_pid_stats_t *local_pid = netdata_ebpf_get_shm_pointer_unsafe(pid, NETDATA_EBPF_PIDS_PROCESS_IDX);
1512 if (!local_pid)
@@ -1509,7 +1536,7 @@ void collect_data_for_all_processes(int tbl_pid_stats_fd, int maps_per_core)
1536 if (tbl_pid_stats_fd == -1)
1537 return;
1538
1512 - pids_fd[NETDATA_EBPF_PIDS_PROCESS_IDX] = tbl_pid_stats_fd;
1539 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_PROCESS_IDX, tbl_pid_stats_fd);
1540 size_t length = sizeof(ebpf_process_stat_t);
1541 if (maps_per_core)
1542 length *= ebpf_nprocs;
@@ -1517,6 +1544,9 @@ void collect_data_for_all_processes(int tbl_pid_stats_fd, int maps_per_core)
1544 if (tbl_pid_stats_fd != -1) {
1545 uint32_t key = 0, next_key = 0;
1546 while (bpf_map_get_next_key(tbl_pid_stats_fd, &key, &next_key) == 0) {
1547 + if (ebpf_plugin_stop())
1548 + break;
1549 +
1550 if (bpf_map_lookup_elem(tbl_pid_stats_fd, &key, process_stat_vector)) {
1551 goto end_process_loop;
1552 }
@@ -1534,13 +1564,12 @@ void collect_data_for_all_processes(int tbl_pid_stats_fd, int maps_per_core)
1564 w->ct = process_stat_vector[0].ct;
1565 w->create_thread = process_stat_vector[0].create_thread;
1566 w->exit_call = process_stat_vector[0].exit_call;
1537 - w->create_thread = process_stat_vector[0].create_thread;
1567 w->create_process = process_stat_vector[0].create_process;
1568 w->release_call = process_stat_vector[0].release_call;
1569 w->task_err = process_stat_vector[0].task_err;
1570 } else {
1571 if (kill((pid_t)key, 0)) { // No PID found
1543 - if (netdata_ebpf_reset_shm_pointer_unsafe(tbl_pid_stats_fd, key, NETDATA_EBPF_PIDS_CACHESTAT_IDX))
1572 + if (netdata_ebpf_reset_shm_pointer_unsafe(tbl_pid_stats_fd, key, NETDATA_EBPF_PIDS_PROCESS_IDX))
1573 memset(w, 0, sizeof(*w));
1574 }
1575 }
@@ -1553,6 +1582,9 @@ void collect_data_for_all_processes(int tbl_pid_stats_fd, int maps_per_core)
1582
1583 struct ebpf_target *w;
1584 for (w = apps_groups_root_target; w; w = w->next) {
1585 + if (ebpf_plugin_stop())
1586 + break;
1587 +
1588 if (unlikely(!(w->processes)))
1589 continue;
1590
@@ -1581,11 +1613,14 @@ static void process_collector(ebpf_module_t *em)
1613 uint32_t running_time = 0;
1614 uint32_t lifetime = em->lifetime;
1615 netdata_idx_t *stats = em->hash_table_stats;
1584 - memset(stats, 0, sizeof(em->hash_table_stats));
1616 + memset(stats, 0, sizeof(netdata_idx_t) * NETDATA_EBPF_GLOBAL_TABLE_STATUS_END);
1617 heartbeat_t hb;
1618 heartbeat_init(&hb, USEC_PER_SEC);
1619 int process_maps_per_core = ebpf_modules[EBPF_MODULE_PROCESS_IDX].maps_per_core;
1620 while (!ebpf_plugin_stop() && running_time < lifetime) {
1621 + if (ebpf_plugin_stop())
1622 + break;
1623 +
1624 heartbeat_next(&hb);
1625
1626 if (ebpf_plugin_stop())
@@ -1599,7 +1634,11 @@ static void process_collector(ebpf_module_t *em)
1634 netdata_apps_integration_flags_t apps_enabled = em->apps_charts;
1635
1636 if (ebpf_all_pids_count > 0) {
1602 - sem_wait(shm_mutex_ebpf_integration);
1637 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
1638 + if (errno != ECANCELED)
1639 + netdata_log_error("PROCESS: Failed to wait on semaphore.");
1640 + break;
1641 + }
1642 netdata_mutex_lock(&collect_data_mutex);
1643 collect_data_for_all_processes(process_pid_fd, process_maps_per_core);
1644
@@ -1607,9 +1646,18 @@ static void process_collector(ebpf_module_t *em)
1646 ebpf_update_process_cgroup();
1647 }
1648 netdata_mutex_unlock(&collect_data_mutex);
1610 - sem_post(shm_mutex_ebpf_integration);
1649 + if (sem_post(shm_mutex_ebpf_integration)) {
1650 + netdata_log_error("PROCESS: Failed to post semaphore.");
1651 + break;
1652 + }
1653 }
1654
1655 + // Avoid acquiring lock for data-send when shutdown is in progress;
1656 + // the main thread may be holding lock for apps-parsing at this point,
1657 + // which would delay this thread's join and compound the shutdown time.
1658 + if (ebpf_plugin_stop())
1659 + break;
1660 +
1661 netdata_mutex_lock(&lock);
1662
1663 if (publish_global) {
@@ -1622,23 +1670,23 @@ static void process_collector(ebpf_module_t *em)
1670 }
1671
1672 if (cgroups && shm_ebpf_cgroup.header) {
1625 - ebpf_process_send_cgroup_data(em);
1673 + if (!ebpf_plugin_stop())
1674 + ebpf_process_send_cgroup_data(em);
1675 }
1676
1677 netdata_mutex_unlock(&collect_data_mutex);
1678 netdata_mutex_unlock(&lock);
1679
1631 - netdata_mutex_lock(&ebpf_exit_cleanup);
1632 - if (running_time && !em->running_time)
1633 - running_time = update_every;
1634 - else
1635 - running_time += update_every;
1680 + fflush(stdout);
1681
1682 + if (ebpf_plugin_stop())
1683 + break;
1684 +
1685 + netdata_mutex_lock(&ebpf_exit_cleanup);
1686 + running_time += update_every;
1687 em->running_time = running_time;
1688 netdata_mutex_unlock(&ebpf_exit_cleanup);
1689 }
1640 -
1641 - fflush(stdout);
1690 }
1691 }
1692
@@ -1665,7 +1713,7 @@ static void ebpf_process_allocate_global_vectors(size_t length)
1713
1714 static void change_syscalls()
1715 {
1668 - static char *lfork = {"do_fork"};
1716 + static char *lfork = "do_fork";
1717 process_id_names[NETDATA_KEY_PUBLISH_PROCESS_FORK] = lfork;
1718 }
1719
@@ -1685,6 +1733,29 @@ static void set_local_pointers()
1733 *
1734 *****************************************************************/
1735
1736 +/**
1737 + * Enable a single tracepoint
1738 + *
1739 + * Enable a tracepoint and store whether it was already enabled.
1740 + *
1741 + * @param event the tracepoint event name
1742 + * @param was_enabled pointer to store the previous state
1743 + *
1744 + * @return 0 on success, -1 on error
1745 + */
1746 +static int ebpf_enable_single_tracepoint(const char *event, int *was_enabled)
1747 +{
1748 + int enabled = ebpf_is_tracepoint_enabled(tracepoint_sched_type, event);
1749 + if (enabled == -1)
1750 + return -1;
1751 + if (!enabled) {
1752 + if (ebpf_enable_tracing_values(tracepoint_sched_type, event))
1753 + return -1;
1754 + }
1755 + *was_enabled = enabled;
1756 + return 0;
1757 +}
1758 +
1759 /**
1760 * Enable tracepoints
1761 *
@@ -1694,32 +1765,14 @@ static void set_local_pointers()
1765 */
1766 static int ebpf_process_enable_tracepoints()
1767 {
1697 - int test = ebpf_is_tracepoint_enabled(tracepoint_sched_type, tracepoint_sched_process_exit);
1698 - if (test == -1)
1768 + if (ebpf_enable_single_tracepoint(tracepoint_sched_process_exit, &was_sched_process_exit_enabled))
1769 return -1;
1700 - else if (!test) {
1701 - if (ebpf_enable_tracing_values(tracepoint_sched_type, tracepoint_sched_process_exit))
1702 - return -1;
1703 - }
1704 - was_sched_process_exit_enabled = test;
1770
1706 - test = ebpf_is_tracepoint_enabled(tracepoint_sched_type, tracepoint_sched_process_exec);
1707 - if (test == -1)
1771 + if (ebpf_enable_single_tracepoint(tracepoint_sched_process_exec, &was_sched_process_exec_enabled))
1772 return -1;
1709 - else if (!test) {
1710 - if (ebpf_enable_tracing_values(tracepoint_sched_type, tracepoint_sched_process_exec))
1711 - return -1;
1712 - }
1713 - was_sched_process_exec_enabled = test;
1773
1715 - test = ebpf_is_tracepoint_enabled(tracepoint_sched_type, tracepoint_sched_process_fork);
1716 - if (test == -1)
1774 + if (ebpf_enable_single_tracepoint(tracepoint_sched_process_fork, &was_sched_process_fork_enabled))
1775 return -1;
1718 - else if (!test) {
1719 - if (ebpf_enable_tracing_values(tracepoint_sched_type, tracepoint_sched_process_fork))
1720 - return -1;
1721 - }
1722 - was_sched_process_fork_enabled = test;
1776
1777 return 0;
1778 }
@@ -1737,8 +1790,18 @@ void ebpf_process_thread(void *ptr)
1790 {
1791 ebpf_module_t *em = (ebpf_module_t *)ptr;
1792
1793 + process_safe_clean = false;
1794 +
1795 CLEANUP_FUNCTION_REGISTER(ebpf_process_exit) cleanup_ptr = em;
1796
1797 + if (!ebpf_module_thread_has_valid_state(em)) {
1798 + em->enabled = em->global_charts = em->apps_charts = em->cgroup_charts = NETDATA_THREAD_EBPF_STOPPING;
1799 + netdata_mutex_lock(&ebpf_exit_cleanup);
1800 + ebpf_update_disabled_plugin_stats(em);
1801 + netdata_mutex_unlock(&ebpf_exit_cleanup);
1802 + return;
1803 + }
1804 +
1805 em->maps = process_maps;
1806
1807 netdata_mutex_lock(&ebpf_exit_cleanup);
@@ -1774,6 +1837,7 @@ void ebpf_process_thread(void *ptr)
1837
1838 netdata_mutex_unlock(&lock);
1839
1840 + process_safe_clean = true;
1841 process_collector(em);
1842
1843 netdata_mutex_lock(&ebpf_exit_cleanup);
src/collectors/ebpf.plugin/ebpf_process.h
+1 -1
@@ -54,7 +54,7 @@ enum netdata_ebpf_stats_order {
54 NETDATA_EBPF_ORDER_STAT_HASH_GLOBAL_TABLE_TOTAL,
55 NETDATA_EBPF_ORDER_STAT_HASH_PID_TABLE_ADDED,
56 NETDATA_EBPF_ORDER_STAT_HASH_PID_TABLE_REMOVED,
57 - NETATA_EBPF_ORDER_STAT_ARAL_BEGIN,
57 + NETDATA_EBPF_ORDER_STAT_ARAL_BEGIN,
58 NETDATA_EBPF_ORDER_FUNCTION_PER_THREAD,
59 };
60
src/collectors/ebpf.plugin/ebpf_shm.c
+137 -67
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_shm.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static char *shm_dimension_name[NETDATA_SHM_END] = {"get", "at", "dt", "ctl"};
8 static netdata_syscall_stat_t shm_aggregated_data[NETDATA_SHM_END];
@@ -232,15 +233,15 @@ static void ebpf_shm_adjust_map(struct shm_bpf *obj, ebpf_module_t *em)
233 static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *em)
234 {
235 netdata_ebpf_targets_t *shmt = em->targets;
235 - netdata_ebpf_program_loaded_t test = shmt[NETDATA_KEY_SHMGET_CALL].mode;
236 + netdata_ebpf_program_loaded_t mode = shmt[NETDATA_KEY_SHMGET_CALL].mode;
237
238 // We are testing only one, because all will have the same behavior
238 - if (test == EBPF_LOAD_TRAMPOLINE) {
239 + if (mode == EBPF_LOAD_TRAMPOLINE) {
240 ebpf_shm_disable_tracepoint(obj);
241 ebpf_disable_probe(obj);
242
243 ebpf_set_trampoline_target(obj);
243 - } else if (test == EBPF_LOAD_PROBE || test == EBPF_LOAD_RETPROBE) {
244 + } else if (mode == EBPF_LOAD_PROBE || mode == EBPF_LOAD_RETPROBE) {
245 ebpf_shm_disable_tracepoint(obj);
246 ebpf_disable_trampoline(obj);
247 } else {
@@ -252,7 +253,7 @@ static inline int ebpf_shm_load_and_attach(struct shm_bpf *obj, ebpf_module_t *e
253
254 int ret = shm_bpf__load(obj);
255 if (!ret) {
255 - if (test != EBPF_LOAD_PROBE && test != EBPF_LOAD_RETPROBE)
256 + if (mode != EBPF_LOAD_PROBE && mode != EBPF_LOAD_RETPROBE)
257 shm_bpf__attach(obj);
258 else
259 ret = ebpf_shm_attach_probe(obj);
@@ -450,6 +451,21 @@ static void ebpf_obsolete_shm_global(ebpf_module_t *em)
451 *
452 * @param ptr thread data.
453 */
454 +void ebpf_shm_unload_bpf(ebpf_module_t *em)
455 +{
456 +#ifdef LIBBPF_MAJOR_VERSION
457 + if (shm_bpf_obj) {
458 + shm_bpf__destroy(shm_bpf_obj);
459 + shm_bpf_obj = NULL;
460 + }
461 +#endif
462 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
463 + ebpf_unload_legacy_code(em->objects, em->probe_links);
464 + em->objects = NULL;
465 + em->probe_links = NULL;
466 + }
467 +}
468 +
469 static void ebpf_shm_exit(void *pptr)
470 {
471 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
@@ -460,10 +476,12 @@ static void ebpf_shm_exit(void *pptr)
476 collect_pids &= ~(1 << EBPF_MODULE_SHM_IDX);
477 netdata_mutex_unlock(&lock);
478
463 - if (ebpf_read_shm.thread)
479 + if (ebpf_read_shm.thread) {
480 nd_thread_signal_cancel(ebpf_read_shm.thread);
481 + nd_thread_join(ebpf_read_shm.thread);
482 + }
483
466 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
484 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
485 netdata_mutex_lock(&lock);
486 if (em->cgroup_charts) {
487 ebpf_obsolete_shm_cgroup_charts(em);
@@ -480,24 +498,11 @@ static void ebpf_shm_exit(void *pptr)
498 netdata_mutex_unlock(&lock);
499 }
500
483 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
484 -
485 -#ifdef LIBBPF_MAJOR_VERSION
486 - if (shm_bpf_obj) {
487 - shm_bpf__destroy(shm_bpf_obj);
488 - shm_bpf_obj = NULL;
489 - }
490 -#endif
491 -
492 - if (em->objects) {
493 - ebpf_unload_legacy_code(em->objects, em->probe_links);
494 - em->objects = NULL;
495 - em->probe_links = NULL;
496 - }
501 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
502 + em->functions.bpf_unload(em);
503
504 netdata_mutex_lock(&ebpf_exit_cleanup);
505 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
500 - ebpf_update_stats(&plugin_statistics, em);
506 netdata_mutex_unlock(&ebpf_exit_cleanup);
507 }
508
@@ -517,17 +522,16 @@ static void shm_apps_accumulator(netdata_ebpf_shm_t *out, int maps_per_core)
522 {
523 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
524 netdata_ebpf_shm_t *total = &out[0];
520 - uint64_t ct = total->ct;
525 for (i = 1; i < end; i++) {
526 + if (ebpf_plugin_stop())
527 + break;
528 +
529 netdata_ebpf_shm_t *w = &out[i];
530 total->get += w->get;
531 total->at += w->at;
532 total->dt += w->dt;
533 total->ctl += w->ctl;
534
528 - if (w->ct > ct)
529 - ct = w->ct;
530 -
535 if (!total->name[0] && w->name[0])
536 strncpyz(total->name, w->name, sizeof(total->name) - 1);
537 }
@@ -540,17 +544,17 @@ static void shm_apps_accumulator(netdata_ebpf_shm_t *out, int maps_per_core)
544 *
545 * @param maps_per_core do I need to read all cores?
546 */
543 -static void ebpf_update_shm_cgroup()
547 +static void ebpf_update_shm_cgroup(void)
548 {
545 - netdata_ebpf_shm_t *cv = shm_vector;
546 - size_t length = sizeof(netdata_publish_shm_t);
547 -
549 ebpf_cgroup_target_t *ect;
550
550 - memset(cv, 0, length);
551 + memset(shm_vector, 0, sizeof(netdata_ebpf_shm_t));
552
553 netdata_mutex_lock(&mutex_cgroup_shm);
554 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
555 + if (ebpf_plugin_stop())
556 + break;
557 +
558 struct pid_on_target2 *pids;
559 for (pids = ect->pids; pids; pids = pids->next) {
560 uint32_t pid = pids->pid;
@@ -584,6 +588,9 @@ static void ebpf_read_shm_apps_table(int maps_per_core)
588
589 uint32_t key = 0, next_key = 0;
590 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
591 + if (ebpf_plugin_stop())
592 + break;
593 +
594 if (bpf_map_lookup_elem(fd, &key, cv)) {
595 goto end_shm_loop;
596 }
@@ -616,7 +623,7 @@ static void ebpf_read_shm_apps_table(int maps_per_core)
623 /**
624 * Send global charts to netdata agent.
625 */
619 -static void shm_send_global()
626 +static void shm_send_global(void)
627 {
628 ebpf_write_begin_chart(NETDATA_EBPF_SYSTEM_GROUP, NETDATA_SHM_GLOBAL_CHART, "");
629 write_chart_dimension(
@@ -679,15 +686,19 @@ static void ebpf_shm_sum_pids(netdata_publish_shm_t *shm, struct ebpf_pid_on_tar
686 }
687
688 /**
682 - * Send data to Netdata calling auxiliary functions.
683 - *
684 - * @param root the target list.
685 -*/
689 + * Send data to Netdata calling auxiliary functions.
690 + *
691 + * @param root the target list.
692 + */
693 void ebpf_shm_send_apps_data(struct ebpf_target *root)
694 {
695 struct ebpf_target *w;
696 +
697 netdata_mutex_lock(&collect_data_mutex);
698 for (w = root; w; w = w->next) {
699 + if (ebpf_plugin_stop())
700 + break;
701 +
702 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SHM_IDX))))
703 continue;
704
@@ -936,6 +947,9 @@ static void ebpf_create_systemd_shm_charts(int update_every)
947
948 ebpf_cgroup_target_t *w;
949 for (w = ebpf_cgroup_pids; w; w = w->next) {
950 + if (ebpf_plugin_stop())
951 + break;
952 +
953 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_SHM_CHART))
954 continue;
955
@@ -957,27 +971,33 @@ static void ebpf_create_systemd_shm_charts(int update_every)
971 *
972 * Send collected data to Netdata.
973 */
960 -static void ebpf_send_systemd_shm_charts()
974 +static void ebpf_send_systemd_shm_charts(void)
975 {
976 + static const char *charts[] = {
977 + NETDATA_SHMGET_CHART, NETDATA_SHMAT_CHART, NETDATA_SHMDT_CHART, NETDATA_SHMCTL_CHART};
978 +
979 ebpf_cgroup_target_t *ect;
980 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
981 + if (ebpf_plugin_stop())
982 + break;
983 +
984 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_SHM_CHART))) {
985 continue;
986 }
987
968 - ebpf_write_begin_chart(ect->name, NETDATA_SHMGET_CHART, "");
988 + ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMGET_CALL], "");
989 write_chart_dimension("calls", (long long)ect->publish_shm.get);
990 ebpf_write_end_chart();
991
972 - ebpf_write_begin_chart(ect->name, NETDATA_SHMAT_CHART, "");
992 + ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMAT_CALL], "");
993 write_chart_dimension("calls", (long long)ect->publish_shm.at);
994 ebpf_write_end_chart();
995
976 - ebpf_write_begin_chart(ect->name, NETDATA_SHMDT_CHART, "");
996 + ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMDT_CALL], "");
997 write_chart_dimension("calls", (long long)ect->publish_shm.dt);
998 ebpf_write_end_chart();
999
980 - ebpf_write_begin_chart(ect->name, NETDATA_SHMCTL_CHART, "");
1000 + ebpf_write_begin_chart(ect->name, charts[NETDATA_KEY_SHMCTL_CALL], "");
1001 write_chart_dimension("calls", (long long)ect->publish_shm.ctl);
1002 ebpf_write_end_chart();
1003 }
@@ -993,19 +1013,22 @@ static void ebpf_send_systemd_shm_charts()
1013 */
1014 static void ebpf_send_specific_shm_data(char *type, netdata_publish_shm_t *values)
1015 {
996 - ebpf_write_begin_chart(type, NETDATA_SHMGET_CHART, "");
1016 + static const char *charts[] = {
1017 + NETDATA_SHMGET_CHART, NETDATA_SHMAT_CHART, NETDATA_SHMDT_CHART, NETDATA_SHMCTL_CHART};
1018 +
1019 + ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMGET_CALL], "");
1020 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMGET_CALL].name, (long long)values->get);
1021 ebpf_write_end_chart();
1022
1000 - ebpf_write_begin_chart(type, NETDATA_SHMAT_CHART, "");
1023 + ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMAT_CALL], "");
1024 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMAT_CALL].name, (long long)values->at);
1025 ebpf_write_end_chart();
1026
1004 - ebpf_write_begin_chart(type, NETDATA_SHMDT_CHART, "");
1027 + ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMDT_CALL], "");
1028 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMDT_CALL].name, (long long)values->dt);
1029 ebpf_write_end_chart();
1030
1008 - ebpf_write_begin_chart(type, NETDATA_SHMCTL_CHART, "");
1031 + ebpf_write_begin_chart(type, charts[NETDATA_KEY_SHMCTL_CALL], "");
1032 write_chart_dimension(shm_publish_aggregated[NETDATA_KEY_SHMCTL_CALL].name, (long long)values->ctl);
1033 ebpf_write_end_chart();
1034 }
@@ -1023,6 +1046,11 @@ void ebpf_shm_send_cgroup_data(int update_every)
1046 ebpf_shm_sum_cgroup_pids(&ect->publish_shm, ect->pids);
1047 }
1048
1049 + if (ebpf_plugin_stop()) {
1050 + netdata_mutex_unlock(&mutex_cgroup_shm);
1051 + return;
1052 + }
1053 +
1054 if (shm_ebpf_cgroup.header->systemd_enabled) {
1055 if (send_cgroup_chart) {
1056 ebpf_create_systemd_shm_charts(update_every);
@@ -1032,6 +1060,9 @@ void ebpf_shm_send_cgroup_data(int update_every)
1060 }
1061
1062 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1063 + if (ebpf_plugin_stop())
1064 + break;
1065 +
1066 if (ect->systemd)
1067 continue;
1068
@@ -1045,7 +1076,7 @@ void ebpf_shm_send_cgroup_data(int update_every)
1076 ebpf_send_specific_shm_data(ect->name, &ect->publish_shm);
1077 } else {
1078 ebpf_obsolete_specific_shm_charts(ect->name, update_every);
1048 - ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_SWAP_CHART;
1079 + ect->flags &= ~NETDATA_EBPF_CGROUP_HAS_SHM_CHART;
1080 }
1081 }
1082 }
@@ -1061,6 +1092,9 @@ void ebpf_shm_resume_apps_data()
1092 struct ebpf_target *w;
1093 netdata_mutex_lock(&collect_data_mutex);
1094 for (w = apps_groups_root_target; w; w = w->next) {
1095 + if (ebpf_plugin_stop())
1096 + break;
1097 +
1098 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SHM_IDX))))
1099 continue;
1100
@@ -1093,30 +1127,48 @@ void ebpf_read_shm_thread(void *ptr)
1127 uint32_t lifetime = em->lifetime;
1128 int cgroups = em->cgroup_charts;
1129 uint32_t running_time = 0;
1096 - pids_fd[NETDATA_EBPF_PIDS_SHM_IDX] = shm_maps[NETDATA_PID_SHM_TABLE].map_fd;
1130 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SHM_IDX, shm_maps[NETDATA_PID_SHM_TABLE].map_fd);
1131 heartbeat_t hb;
1098 - heartbeat_init(&hb, update_every * USEC_PER_SEC);
1132 + heartbeat_init(&hb, USEC_PER_SEC);
1133 while (!ebpf_plugin_stop() && running_time < lifetime) {
1134 + if (ebpf_plugin_stop())
1135 + break;
1136 +
1137 (void)heartbeat_next(&hb);
1101 - if (ebpf_plugin_stop() || ++counter != update_every)
1138 + if (ebpf_plugin_stop())
1139 + break;
1140 +
1141 + if (++counter != update_every)
1142 continue;
1143
1104 - sem_wait(shm_mutex_ebpf_integration);
1144 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
1145 + if (errno != ECANCELED)
1146 + netdata_log_error("SHM: Failed to wait on semaphore.");
1147 + break;
1148 + }
1149 ebpf_read_shm_apps_table(maps_per_core);
1150 ebpf_shm_resume_apps_data();
1151 + if (ebpf_plugin_stop()) {
1152 + if (sem_post(shm_mutex_ebpf_integration))
1153 + netdata_log_error("SHM: Failed to post semaphore.");
1154 + break;
1155 + }
1156 +
1157 if (cgroups && shm_ebpf_cgroup.header)
1158 ebpf_update_shm_cgroup();
1159
1110 - sem_post(shm_mutex_ebpf_integration);
1160 + if (sem_post(shm_mutex_ebpf_integration)) {
1161 + netdata_log_error("SHM: Failed to post semaphore.");
1162 + break;
1163 + }
1164
1165 counter = 0;
1166
1114 - netdata_mutex_lock(&ebpf_exit_cleanup);
1115 - if (running_time && !em->running_time)
1116 - running_time = update_every;
1117 - else
1118 - running_time += update_every;
1167 + if (ebpf_plugin_stop())
1168 + break;
1169
1170 + netdata_mutex_lock(&ebpf_exit_cleanup);
1171 + running_time += update_every;
1172 em->running_time = running_time;
1173 netdata_mutex_unlock(&ebpf_exit_cleanup);
1174 }
@@ -1138,9 +1190,15 @@ static void shm_collector(ebpf_module_t *em)
1190 heartbeat_t hb;
1191 heartbeat_init(&hb, USEC_PER_SEC);
1192 while (!ebpf_plugin_stop() && running_time < lifetime) {
1193 + if (ebpf_plugin_stop())
1194 + break;
1195 +
1196 heartbeat_next(&hb);
1197
1143 - if (ebpf_plugin_stop() || ++counter != update_every)
1198 + if (ebpf_plugin_stop())
1199 + break;
1200 +
1201 + if (++counter != update_every)
1202 continue;
1203
1204 counter = 0;
@@ -1154,18 +1212,22 @@ static void shm_collector(ebpf_module_t *em)
1212 ebpf_shm_send_apps_data(apps_groups_root_target);
1213 }
1214
1215 + if (ebpf_plugin_stop()) {
1216 + netdata_mutex_unlock(&lock);
1217 + break;
1218 + }
1219 +
1220 if (cgroups && shm_ebpf_cgroup.header) {
1221 ebpf_shm_send_cgroup_data(update_every);
1222 }
1223
1224 netdata_mutex_unlock(&lock);
1225
1163 - netdata_mutex_lock(&ebpf_exit_cleanup);
1164 - if (running_time && !em->running_time)
1165 - running_time = update_every;
1166 - else
1167 - running_time += update_every;
1226 + if (ebpf_plugin_stop())
1227 + break;
1228
1229 + netdata_mutex_lock(&ebpf_exit_cleanup);
1230 + running_time += update_every;
1231 em->running_time = running_time;
1232 netdata_mutex_unlock(&ebpf_exit_cleanup);
1233 }
@@ -1269,10 +1331,9 @@ void ebpf_shm_create_apps_charts(struct ebpf_module *em, void *ptr)
1331 *
1332 * @param apps is apps enabled?
1333 */
1272 -static void ebpf_shm_allocate_global_vectors(int apps)
1334 +static void ebpf_shm_allocate_global_vectors(void)
1335 {
1274 - UNUSED(apps);
1275 - shm_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_publish_shm_t));
1336 + shm_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_ebpf_shm_t));
1337 shm_values = callocz((size_t)ebpf_nprocs, sizeof(netdata_idx_t));
1338
1339 memset(shm_hash_values, 0, sizeof(shm_hash_values));
@@ -1336,8 +1397,13 @@ static int ebpf_shm_load_bpf(ebpf_module_t *em)
1397 shm_bpf_obj = shm_bpf__open();
1398 if (!shm_bpf_obj)
1399 ret = -1;
1339 - else
1400 + else {
1401 ret = ebpf_shm_load_and_attach(shm_bpf_obj, em);
1402 + if (ret) {
1403 + shm_bpf__destroy(shm_bpf_obj);
1404 + shm_bpf_obj = NULL;
1405 + }
1406 + }
1407 }
1408 #endif
1409
@@ -1355,11 +1421,15 @@ static int ebpf_shm_load_bpf(ebpf_module_t *em)
1421 */
1422 void ebpf_shm_thread(void *ptr)
1423 {
1358 - pids_fd[NETDATA_EBPF_PIDS_SHM_IDX] = -1;
1424 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SHM_IDX, -1);
1425 ebpf_module_t *em = (ebpf_module_t *)ptr;
1426
1427 CLEANUP_FUNCTION_REGISTER(ebpf_shm_exit) cleanup_ptr = em;
1428
1429 + if (!ebpf_module_thread_has_valid_state(em)) {
1430 + goto endshm;
1431 + }
1432 +
1433 em->maps = shm_maps;
1434
1435 ebpf_update_pid_table(&shm_maps[NETDATA_PID_SHM_TABLE], em);
@@ -1371,7 +1441,7 @@ void ebpf_shm_thread(void *ptr)
1441 goto endshm;
1442 }
1443
1374 - ebpf_shm_allocate_global_vectors(em->apps_charts);
1444 + ebpf_shm_allocate_global_vectors();
1445
1446 int algorithms[NETDATA_SHM_END] = {
1447 NETDATA_EBPF_INCREMENTAL_IDX,
src/collectors/ebpf.plugin/ebpf_shm.h
-1
@@ -43,7 +43,6 @@ enum shm_counters {
43
44 void ebpf_shm_thread(void *ptr);
45 void ebpf_shm_create_apps_charts(struct ebpf_module *em, void *ptr);
46 -void ebpf_shm_release(netdata_publish_shm_t *stat);
46 extern netdata_ebpf_targets_t shm_targets[];
47
48 extern struct config shm_config;
src/collectors/ebpf.plugin/ebpf_socket.c
+181 -94
@@ -4,6 +4,7 @@
4
5 #include "ebpf.h"
6 #include "ebpf_socket.h"
7 +#include "libbpf_api/ebpf_library.h"
8
9 /*****************************************************************
10 *
@@ -123,6 +124,8 @@ struct netdata_static_thread ebpf_read_socket = {
124
125 ARAL *aral_socket_table = NULL;
126
127 +#define NETDATA_MAX(a, b) ((a) > (b) ? (a) : (b))
128 +
129 #ifdef LIBBPF_MAJOR_VERSION
130 /**
131 * Disable Probe
@@ -190,7 +193,7 @@ static void ebpf_set_trampoline_target(struct socket_bpf *obj)
193 bpf_program__set_attach_target(
194 obj->progs.netdata_tcp_v4_connect_fexit, 0, socket_targets[NETDATA_FCNT_TCP_V4_CONNECT].name);
195
193 - if (tcp_v6_connect_address.type == 'T') {
196 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
197 bpf_program__set_attach_target(
198 obj->progs.netdata_tcp_v6_connect_fentry, 0, socket_targets[NETDATA_FCNT_TCP_V6_CONNECT].name);
199
@@ -284,90 +287,118 @@ static long ebpf_socket_attach_probes(struct socket_bpf *obj, netdata_run_mode_t
287 obj->links.netdata_inet_csk_accept_kretprobe = bpf_program__attach_kprobe(
288 obj->progs.netdata_inet_csk_accept_kretprobe, true, socket_targets[NETDATA_FCNT_INET_CSK_ACCEPT].name);
289 long ret = libbpf_get_error(obj->links.netdata_inet_csk_accept_kretprobe);
287 - if (ret)
290 + if (ret) {
291 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_INET_CSK_ACCEPT].name);
292 return -1;
293 + }
294
295 obj->links.netdata_tcp_retransmit_skb_kprobe = bpf_program__attach_kprobe(
296 obj->progs.netdata_tcp_retransmit_skb_kprobe, false, socket_targets[NETDATA_FCNT_TCP_RETRANSMIT].name);
297 ret = libbpf_get_error(obj->links.netdata_tcp_retransmit_skb_kprobe);
293 - if (ret)
298 + if (ret) {
299 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_TCP_RETRANSMIT].name);
300 return -1;
301 + }
302
303 obj->links.netdata_tcp_cleanup_rbuf_kprobe = bpf_program__attach_kprobe(
304 obj->progs.netdata_tcp_cleanup_rbuf_kprobe, false, socket_targets[NETDATA_FCNT_CLEANUP_RBUF].name);
305 ret = libbpf_get_error(obj->links.netdata_tcp_cleanup_rbuf_kprobe);
299 - if (ret)
306 + if (ret) {
307 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_CLEANUP_RBUF].name);
308 return -1;
309 + }
310
311 obj->links.netdata_tcp_close_kprobe = bpf_program__attach_kprobe(
312 obj->progs.netdata_tcp_close_kprobe, false, socket_targets[NETDATA_FCNT_TCP_CLOSE].name);
313 ret = libbpf_get_error(obj->links.netdata_tcp_close_kprobe);
305 - if (ret)
314 + if (ret) {
315 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_TCP_CLOSE].name);
316 return -1;
317 + }
318
319 obj->links.netdata_udp_recvmsg_kprobe = bpf_program__attach_kprobe(
320 obj->progs.netdata_udp_recvmsg_kprobe, false, socket_targets[NETDATA_FCNT_UDP_RECEVMSG].name);
321 ret = libbpf_get_error(obj->links.netdata_udp_recvmsg_kprobe);
311 - if (ret)
322 + if (ret) {
323 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_UDP_RECEVMSG].name);
324 return -1;
325 + }
326
327 obj->links.netdata_udp_recvmsg_kretprobe = bpf_program__attach_kprobe(
328 obj->progs.netdata_udp_recvmsg_kretprobe, true, socket_targets[NETDATA_FCNT_UDP_RECEVMSG].name);
329 ret = libbpf_get_error(obj->links.netdata_udp_recvmsg_kretprobe);
317 - if (ret)
330 + if (ret) {
331 + collector_error("Cannot attach kretprobe for %s", socket_targets[NETDATA_FCNT_UDP_RECEVMSG].name);
332 return -1;
333 + }
334
335 if (sel == MODE_RETURN) {
336 obj->links.netdata_tcp_sendmsg_kretprobe = bpf_program__attach_kprobe(
337 obj->progs.netdata_tcp_sendmsg_kretprobe, true, socket_targets[NETDATA_FCNT_TCP_SENDMSG].name);
338 ret = libbpf_get_error(obj->links.netdata_tcp_sendmsg_kretprobe);
324 - if (ret)
339 + if (ret) {
340 + collector_error("Cannot attach kretprobe for %s", socket_targets[NETDATA_FCNT_TCP_SENDMSG].name);
341 return -1;
342 + }
343
344 obj->links.netdata_udp_sendmsg_kretprobe = bpf_program__attach_kprobe(
345 obj->progs.netdata_udp_sendmsg_kretprobe, true, socket_targets[NETDATA_FCNT_UDP_SENDMSG].name);
346 ret = libbpf_get_error(obj->links.netdata_udp_sendmsg_kretprobe);
330 - if (ret)
347 + if (ret) {
348 + collector_error("Cannot attach kretprobe for %s", socket_targets[NETDATA_FCNT_UDP_SENDMSG].name);
349 return -1;
350 + }
351
352 obj->links.netdata_tcp_v4_connect_kretprobe = bpf_program__attach_kprobe(
353 obj->progs.netdata_tcp_v4_connect_kretprobe, true, socket_targets[NETDATA_FCNT_TCP_V4_CONNECT].name);
354 ret = libbpf_get_error(obj->links.netdata_tcp_v4_connect_kretprobe);
336 - if (ret)
355 + if (ret) {
356 + collector_error("Cannot attach kretprobe for %s", socket_targets[NETDATA_FCNT_TCP_V4_CONNECT].name);
357 return -1;
358 + }
359
339 - if (tcp_v6_connect_address.type == 'T') {
360 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
361 obj->links.netdata_tcp_v6_connect_kretprobe = bpf_program__attach_kprobe(
362 obj->progs.netdata_tcp_v6_connect_kretprobe, true, socket_targets[NETDATA_FCNT_TCP_V6_CONNECT].name);
363 ret = libbpf_get_error(obj->links.netdata_tcp_v6_connect_kretprobe);
343 - if (ret)
364 + if (ret) {
365 + collector_error("Cannot attach kretprobe for %s", socket_targets[NETDATA_FCNT_TCP_V6_CONNECT].name);
366 return -1;
367 + }
368 }
369 } else {
370 obj->links.netdata_tcp_sendmsg_kprobe = bpf_program__attach_kprobe(
371 obj->progs.netdata_tcp_sendmsg_kprobe, false, socket_targets[NETDATA_FCNT_TCP_SENDMSG].name);
372 ret = libbpf_get_error(obj->links.netdata_tcp_sendmsg_kprobe);
350 - if (ret)
373 + if (ret) {
374 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_TCP_SENDMSG].name);
375 return -1;
376 + }
377
378 obj->links.netdata_udp_sendmsg_kprobe = bpf_program__attach_kprobe(
379 obj->progs.netdata_udp_sendmsg_kprobe, false, socket_targets[NETDATA_FCNT_UDP_SENDMSG].name);
380 ret = libbpf_get_error(obj->links.netdata_udp_sendmsg_kprobe);
356 - if (ret)
381 + if (ret) {
382 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_UDP_SENDMSG].name);
383 return -1;
384 + }
385
386 obj->links.netdata_tcp_v4_connect_kprobe = bpf_program__attach_kprobe(
387 obj->progs.netdata_tcp_v4_connect_kprobe, false, socket_targets[NETDATA_FCNT_TCP_V4_CONNECT].name);
388 ret = libbpf_get_error(obj->links.netdata_tcp_v4_connect_kprobe);
362 - if (ret)
389 + if (ret) {
390 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_TCP_V4_CONNECT].name);
391 return -1;
392 + }
393
365 - if (tcp_v6_connect_address.type == 'T') {
394 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
395 obj->links.netdata_tcp_v6_connect_kprobe = bpf_program__attach_kprobe(
396 obj->progs.netdata_tcp_v6_connect_kprobe, false, socket_targets[NETDATA_FCNT_TCP_V6_CONNECT].name);
397 ret = libbpf_get_error(obj->links.netdata_tcp_v6_connect_kprobe);
369 - if (ret)
398 + if (ret) {
399 + collector_error("Cannot attach kprobe for %s", socket_targets[NETDATA_FCNT_TCP_V6_CONNECT].name);
400 return -1;
401 + }
402 }
403 }
404
@@ -496,7 +527,6 @@ static void ebpf_socket_free(ebpf_module_t *em)
527 netdata_mutex_lock(&ebpf_exit_cleanup);
528 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
529 ebpf_update_stats(&plugin_statistics, em);
499 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
530 netdata_mutex_unlock(&ebpf_exit_cleanup);
531
532 netdata_mutex_lock(&lock);
@@ -513,7 +543,7 @@ static void ebpf_socket_free(ebpf_module_t *em)
543 **/
544 static void ebpf_obsolete_systemd_socket_charts(int update_every, char *id)
545 {
516 - int order = 20080;
546 + int order = NETDATA_SOCKET_SYSTEMD_ORDER_BASE;
547 ebpf_write_chart_obsolete(
548 id,
549 NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V4,
@@ -526,7 +556,7 @@ static void ebpf_obsolete_systemd_socket_charts(int update_every, char *id)
556 order++,
557 update_every);
558
529 - if (tcp_v6_connect_address.type == 'T') {
559 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
560 ebpf_write_chart_obsolete(
561 id,
562 NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V6,
@@ -647,7 +677,7 @@ static inline void ebpf_obsolete_socket_cgroup_charts(ebpf_module_t *em)
677 */
678 void ebpf_socket_obsolete_apps_charts(struct ebpf_module *em)
679 {
650 - int order = 20130;
680 + int order = NETDATA_SOCKET_APPS_ORDER_BASE;
681 struct ebpf_target *w;
682 int update_every = em->update_every;
683 netdata_mutex_lock(&collect_data_mutex);
@@ -667,7 +697,7 @@ void ebpf_socket_obsolete_apps_charts(struct ebpf_module *em)
697 order++,
698 update_every);
699
670 - if (tcp_v6_connect_address.type == 'T') {
700 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
701 ebpf_write_chart_obsolete(
702 NETDATA_APP_FAMILY,
703 w->clean_name,
@@ -767,7 +797,7 @@ void ebpf_socket_obsolete_apps_charts(struct ebpf_module *em)
797 */
798 static void ebpf_socket_obsolete_global_charts(ebpf_module_t *em)
799 {
770 - int order = 21070;
800 + int order = NETDATA_SOCKET_CHART_ORDER_BASE;
801 ebpf_write_chart_obsolete(
802 NETDATA_EBPF_IP_FAMILY,
803 NETDATA_INBOUND_CONNECTIONS,
@@ -895,10 +925,12 @@ static void ebpf_socket_exit(void *pptr)
925 if (!em)
926 return;
927
898 - if (ebpf_read_socket.thread)
928 + if (ebpf_read_socket.thread) {
929 nd_thread_signal_cancel(ebpf_read_socket.thread);
930 + nd_thread_join(ebpf_read_socket.thread);
931 + }
932
901 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
933 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
934 netdata_mutex_lock(&lock);
935
936 if (em->cgroup_charts) {
@@ -916,6 +948,9 @@ static void ebpf_socket_exit(void *pptr)
948 netdata_mutex_unlock(&lock);
949 }
950
951 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
952 + em->functions.bpf_unload(em);
953 +
954 ebpf_socket_free(em);
955 }
956
@@ -942,11 +977,10 @@ static void ebpf_update_global_publish(
977 netdata_publish_syscall_t *move = publish;
978 while (move) {
979 if (input->call != move->pcall) {
945 - // This condition happens to avoid initial values with dimensions higher than normal values.
980 if (move->pcall) {
981 move->ncall = (input->call > move->pcall) ? input->call - move->pcall : move->pcall - input->call;
982 move->nbyte = (input->bytes > move->pbyte) ? input->bytes - move->pbyte : move->pbyte - input->bytes;
949 - move->nerr = (input->ecall > move->nerr) ? input->ecall - move->perr : move->perr - input->ecall;
983 + move->nerr = (input->ecall > move->perr) ? input->ecall - move->perr : move->perr - input->ecall;
984 } else {
985 move->ncall = 0;
986 move->nbyte = 0;
@@ -984,7 +1018,7 @@ static void ebpf_update_global_publish(
1018 */
1019 static inline collected_number ebpf_socket_bytes2bits(uint64_t value)
1020 {
987 - return (collected_number)(value * 8 / BITS_IN_A_KILOBIT);
1021 + return value * 8 / BITS_IN_A_KILOBIT;
1022 }
1023
1024 /**
@@ -1069,6 +1103,9 @@ void ebpf_socket_send_apps_data()
1103 struct ebpf_target *w;
1104 netdata_mutex_lock(&collect_data_mutex);
1105 for (w = apps_groups_root_target; w; w = w->next) {
1106 + if (ebpf_plugin_stop())
1107 + break;
1108 +
1109 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SOCKET_IDX))))
1110 continue;
1111
@@ -1077,7 +1114,7 @@ void ebpf_socket_send_apps_data()
1114 write_chart_dimension("connections", (collected_number)values->call_tcp_v4_connection);
1115 ebpf_write_end_chart();
1116
1080 - if (tcp_v6_connect_address.type == 'T') {
1117 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
1118 ebpf_write_begin_chart(NETDATA_APP_FAMILY, w->clean_name, "_call_tcp_v6_connection");
1119 write_chart_dimension("connections", (collected_number)values->call_tcp_v6_connection);
1120 ebpf_write_end_chart();
@@ -1127,7 +1164,7 @@ void ebpf_socket_send_apps_data()
1164 */
1165 static void ebpf_socket_create_global_charts(ebpf_module_t *em)
1166 {
1130 - int order = 21070;
1167 + int order = NETDATA_SOCKET_CHART_ORDER_BASE;
1168 ebpf_create_chart(
1169 NETDATA_EBPF_IP_FAMILY,
1170 NETDATA_INBOUND_CONNECTIONS,
@@ -1282,9 +1319,12 @@ void ebpf_socket_create_apps_charts(struct ebpf_module *em, void *ptr)
1319 {
1320 struct ebpf_target *root = ptr;
1321 struct ebpf_target *w;
1285 - int order = 20130;
1322 + int order = NETDATA_SOCKET_APPS_ORDER_BASE;
1323 int update_every = em->update_every;
1324 for (w = root; w; w = w->next) {
1325 + if (ebpf_plugin_stop())
1326 + break;
1327 +
1328 if (unlikely(!w->exposed))
1329 continue;
1330
@@ -1304,7 +1344,7 @@ void ebpf_socket_create_apps_charts(struct ebpf_module *em, void *ptr)
1344 ebpf_commit_label();
1345 fprintf(stdout, "DIMENSION connections '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
1346
1307 - if (tcp_v6_connect_address.type == 'T') {
1347 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
1348 ebpf_write_chart_cmd(
1349 NETDATA_APP_FAMILY,
1350 w->clean_name,
@@ -1587,6 +1627,9 @@ static void ebpf_hash_socket_accumulator(netdata_socket_t *values, int end)
1627 uint16_t family = AF_UNSPEC;
1628 uint32_t external_origin = values[0].external_origin;
1629 for (i = 1; i < end; i++) {
1630 + if (ebpf_plugin_stop())
1631 + break;
1632 +
1633 netdata_socket_t *w = &values[i];
1634
1635 values[0].tcp.call_tcp_sent += w->tcp.call_tcp_sent;
@@ -1761,7 +1804,7 @@ static void ebpf_update_array_vectors(ebpf_module_t *em)
1804
1805 netdata_socket_t *values = socket_values;
1806 size_t length = sizeof(netdata_socket_t);
1764 - int test, end;
1807 + int ret, end;
1808 if (maps_per_core) {
1809 length *= ebpf_nprocs;
1810 end = ebpf_nprocs;
@@ -1774,9 +1817,12 @@ static void ebpf_update_array_vectors(ebpf_module_t *em)
1817 memset(values, 0, length);
1818 time_t update_time = time(NULL);
1819 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
1777 - test = bpf_map_lookup_elem(fd, &key, values);
1820 + if (ebpf_plugin_stop())
1821 + break;
1822 +
1823 + ret = bpf_map_lookup_elem(fd, &key, values);
1824 bool deleted = true;
1779 - if (test < 0) {
1825 + if (ret < 0) {
1826 goto end_socket_loop;
1827 }
1828
@@ -1786,14 +1832,6 @@ static void ebpf_update_array_vectors(ebpf_module_t *em)
1832
1833 ebpf_hash_socket_accumulator(values, end);
1834
1789 - // We update UDP to show info with charts, but we do not show them with functions
1790 - /*
1791 - if (key.dport == NETDATA_EBPF_UDP_PORT && values[0].protocol == IPPROTO_UDP) {
1792 - bpf_map_delete_elem(fd, &key);
1793 - goto end_socket_loop;
1794 - }
1795 - */
1796 -
1835 // Discard non-bind sockets
1836 if (!key.daddr.addr64[0] && !key.daddr.addr64[1] && !key.saddr.addr64[0] && !key.saddr.addr64[1]) {
1837 bpf_map_delete_elem(fd, &key);
@@ -1849,7 +1887,7 @@ static void ebpf_update_array_vectors(ebpf_module_t *em)
1887 rw_spinlock_write_unlock(&pid_ptr->socket_stats.rw_spinlock);
1888 rw_spinlock_write_unlock(&ebpf_judy_pid.index.rw_spinlock);
1889
1852 - end_socket_loop: ;// the empty statement is here to allow code to be compiled by old compilers
1890 + end_socket_loop:; // the empty statement is here to allow code to be compiled by old compilers
1891 netdata_ebpf_pid_stats_t *local_pid =
1892 netdata_ebpf_get_shm_pointer_unsafe(key.pid, NETDATA_EBPF_PIDS_SOCKET_IDX);
1893 if (!local_pid)
@@ -1876,6 +1914,9 @@ void ebpf_socket_resume_apps_data()
1914
1915 netdata_mutex_lock(&collect_data_mutex);
1916 for (w = apps_groups_root_target; w; w = w->next) {
1917 + if (ebpf_plugin_stop())
1918 + break;
1919 +
1920 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SOCKET_IDX))))
1921 continue;
1922
@@ -1917,6 +1958,9 @@ static void ebpf_update_socket_cgroup()
1958
1959 netdata_mutex_lock(&mutex_cgroup_shm);
1960 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1961 + if (ebpf_plugin_stop())
1962 + break;
1963 +
1964 struct pid_on_target2 *pids;
1965 for (pids = ect->pids; pids; pids = pids->next) {
1966 uint32_t pid = pids->pid;
@@ -1968,21 +2012,43 @@ void ebpf_read_socket_thread(void *ptr)
2012 uint32_t lifetime = em->lifetime;
2013 int cgroups = em->cgroup_charts;
2014 heartbeat_t hb;
1971 - heartbeat_init(&hb, update_every * USEC_PER_SEC);
2015 + heartbeat_init(&hb, USEC_PER_SEC);
2016 while (!ebpf_plugin_stop() && running_time < lifetime) {
2017 + if (ebpf_plugin_stop())
2018 + break;
2019 +
2020 heartbeat_next(&hb);
1974 - if (ebpf_plugin_stop() || ++counter != update_every)
2021 + if (ebpf_plugin_stop())
2022 + break;
2023 +
2024 + if (++counter != update_every)
2025 continue;
2026
1977 - sem_wait(shm_mutex_ebpf_integration);
2027 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
2028 + if (errno != ECANCELED)
2029 + netdata_log_error("SOCKET: Failed to wait on semaphore.");
2030 + break;
2031 + }
2032 ebpf_update_array_vectors(em);
2033 ebpf_socket_resume_apps_data();
2034 + if (ebpf_plugin_stop()) {
2035 + if (sem_post(shm_mutex_ebpf_integration))
2036 + netdata_log_error("SOCKET: Failed to post semaphore.");
2037 + break;
2038 + }
2039 +
2040 if (cgroups && shm_ebpf_cgroup.header)
2041 ebpf_update_socket_cgroup();
2042
1983 - sem_post(shm_mutex_ebpf_integration);
2043 + if (sem_post(shm_mutex_ebpf_integration)) {
2044 + netdata_log_error("SOCKET: Failed to post semaphore.");
2045 + break;
2046 + }
2047
2048 counter = 0;
2049 +
2050 + if (ebpf_plugin_stop())
2051 + break;
2052 }
2053 }
2054
@@ -2059,8 +2125,11 @@ static void read_listen_table()
2125 int fd = socket_maps[NETDATA_SOCKET_LPORTS].map_fd;
2126 netdata_passive_connection_t value = {};
2127 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
2062 - int test = bpf_map_lookup_elem(fd, &key, &value);
2063 - if (test < 0) {
2128 + if (ebpf_plugin_stop())
2129 + break;
2130 +
2131 + int ret = bpf_map_lookup_elem(fd, &key, &value);
2132 + if (ret < 0) {
2133 key = next_key;
2134 continue;
2135 }
@@ -2146,7 +2215,6 @@ void ebpf_socket_fill_publish_apps(ebpf_socket_publish_apps_t *curr, netdata_soc
2215 curr->call_udp_received = ns->udp.call_udp_received;
2216 }
2217
2149 -
2218 /**
2219 * Sum PIDs
2220 *
@@ -2177,27 +2245,16 @@ static void ebpf_socket_sum_cgroup_pids(ebpf_socket_publish_apps_t *socket, stru
2245 pids = pids->next;
2246 }
2247
2180 - socket->bytes_sent = (accumulator.bytes_sent >= socket->bytes_sent) ? accumulator.bytes_sent : socket->bytes_sent;
2181 - socket->bytes_received =
2182 - (accumulator.bytes_received >= socket->bytes_received) ? accumulator.bytes_received : socket->bytes_received;
2183 - socket->call_tcp_sent =
2184 - (accumulator.call_tcp_sent >= socket->call_tcp_sent) ? accumulator.call_tcp_sent : socket->call_tcp_sent;
2185 - socket->call_tcp_received = (accumulator.call_tcp_received >= socket->call_tcp_received) ?
2186 - accumulator.call_tcp_received :
2187 - socket->call_tcp_received;
2188 - socket->retransmit = (accumulator.retransmit >= socket->retransmit) ? accumulator.retransmit : socket->retransmit;
2189 - socket->call_udp_sent =
2190 - (accumulator.call_udp_sent >= socket->call_udp_sent) ? accumulator.call_udp_sent : socket->call_udp_sent;
2191 - socket->call_udp_received = (accumulator.call_udp_received >= socket->call_udp_received) ?
2192 - accumulator.call_udp_received :
2193 - socket->call_udp_received;
2194 - socket->call_close = (accumulator.call_close >= socket->call_close) ? accumulator.call_close : socket->call_close;
2195 - socket->call_tcp_v4_connection = (accumulator.call_tcp_v4_connection >= socket->call_tcp_v4_connection) ?
2196 - accumulator.call_tcp_v4_connection :
2197 - socket->call_tcp_v4_connection;
2198 - socket->call_tcp_v6_connection = (accumulator.call_tcp_v6_connection >= socket->call_tcp_v6_connection) ?
2199 - accumulator.call_tcp_v6_connection :
2200 - socket->call_tcp_v6_connection;
2248 + socket->bytes_sent = NETDATA_MAX(accumulator.bytes_sent, socket->bytes_sent);
2249 + socket->bytes_received = NETDATA_MAX(accumulator.bytes_received, socket->bytes_received);
2250 + socket->call_tcp_sent = NETDATA_MAX(accumulator.call_tcp_sent, socket->call_tcp_sent);
2251 + socket->call_tcp_received = NETDATA_MAX(accumulator.call_tcp_received, socket->call_tcp_received);
2252 + socket->retransmit = NETDATA_MAX(accumulator.retransmit, socket->retransmit);
2253 + socket->call_udp_sent = NETDATA_MAX(accumulator.call_udp_sent, socket->call_udp_sent);
2254 + socket->call_udp_received = NETDATA_MAX(accumulator.call_udp_received, socket->call_udp_received);
2255 + socket->call_close = NETDATA_MAX(accumulator.call_close, socket->call_close);
2256 + socket->call_tcp_v4_connection = NETDATA_MAX(accumulator.call_tcp_v4_connection, socket->call_tcp_v4_connection);
2257 + socket->call_tcp_v6_connection = NETDATA_MAX(accumulator.call_tcp_v6_connection, socket->call_tcp_v6_connection);
2258 }
2259
2260 /**
@@ -2210,7 +2267,7 @@ static void ebpf_socket_sum_cgroup_pids(ebpf_socket_publish_apps_t *socket, stru
2267 */
2268 static void ebpf_create_specific_socket_charts(char *type, int update_every)
2269 {
2213 - int order_basis = 5300;
2270 + int order_basis = NETDATA_SOCKET_CGROUP_ORDER_BASE;
2271 char *label = (!strncmp(type, "cgroup_", 7)) ? &type[7] : type;
2272 ebpf_write_chart_cmd(
2273 type,
@@ -2228,7 +2285,7 @@ static void ebpf_create_specific_socket_charts(char *type, int update_every)
2285 ebpf_commit_label();
2286 fprintf(stdout, "DIMENSION connections '' %s 1 1\n", ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX]);
2287
2231 - if (tcp_v6_connect_address.type == 'T') {
2288 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
2289 ebpf_write_chart_cmd(
2290 type,
2291 NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V6,
@@ -2354,7 +2411,7 @@ static void ebpf_create_specific_socket_charts(char *type, int update_every)
2411 */
2412 static void ebpf_obsolete_specific_socket_charts(char *type, int update_every)
2413 {
2357 - int order_basis = 5300;
2414 + int order_basis = NETDATA_SOCKET_CGROUP_ORDER_BASE;
2415 ebpf_write_chart_obsolete(
2416 type,
2417 NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V4,
@@ -2367,7 +2424,7 @@ static void ebpf_obsolete_specific_socket_charts(char *type, int update_every)
2424 NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + order_basis++,
2425 update_every);
2426
2370 - if (tcp_v6_connect_address.type == 'T') {
2427 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
2428 ebpf_write_chart_obsolete(
2429 type,
2430 NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V6,
@@ -2468,7 +2525,7 @@ static void ebpf_send_specific_socket_data(char *type, ebpf_socket_publish_apps_
2525 write_chart_dimension("connections", (long long)values->call_tcp_v4_connection);
2526 ebpf_write_end_chart();
2527
2471 - if (tcp_v6_connect_address.type == 'T') {
2528 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
2529 ebpf_write_begin_chart(type, NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V6, "");
2530 write_chart_dimension("connections", (long long)values->call_tcp_v6_connection);
2531 ebpf_write_end_chart();
@@ -2620,6 +2677,9 @@ static void ebpf_create_systemd_socket_charts(int update_every)
2677
2678 ebpf_cgroup_target_t *w;
2679 for (w = ebpf_cgroup_pids; w; w = w->next) {
2680 + if (ebpf_plugin_stop())
2681 + break;
2682 +
2683 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_SOCKET_CHART))
2684 continue;
2685
@@ -2627,7 +2687,7 @@ static void ebpf_create_systemd_socket_charts(int update_every)
2687 data_tcp_retransmit.id = data_udp_send.id = data_udp_recv.id = w->name;
2688
2689 ebpf_create_charts_on_systemd(&data_tcp_v4);
2630 - if (tcp_v6_connect_address.type == 'T') {
2690 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
2691 ebpf_create_charts_on_systemd(&data_tcp_v6);
2692 }
2693
@@ -2656,6 +2716,9 @@ static void ebpf_send_systemd_socket_charts()
2716 {
2717 ebpf_cgroup_target_t *ect;
2718 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
2719 + if (ebpf_plugin_stop())
2720 + break;
2721 +
2722 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_SOCKET_CHART))) {
2723 continue;
2724 }
@@ -2664,7 +2727,7 @@ static void ebpf_send_systemd_socket_charts()
2727 write_chart_dimension("connections", (long long)ect->publish_socket.call_tcp_v4_connection);
2728 ebpf_write_end_chart();
2729
2667 - if (tcp_v6_connect_address.type == 'T') {
2730 + if (tcp_v6_connect_address.type == TCP_V6_CONNECT_TYPE) {
2731 ebpf_write_begin_chart(ect->name, NETDATA_SOCK_ID_OR_SUFFIX_CONNECTION_TCP_V6, "");
2732 write_chart_dimension("connections", (long long)ect->publish_socket.call_tcp_v6_connection);
2733 ebpf_write_end_chart();
@@ -2724,6 +2787,11 @@ static void ebpf_socket_send_cgroup_data(int update_every)
2787 ebpf_socket_sum_cgroup_pids(&ect->publish_socket, ect->pids);
2788 }
2789
2790 + if (ebpf_plugin_stop()) {
2791 + netdata_mutex_unlock(&mutex_cgroup_shm);
2792 + return;
2793 + }
2794 +
2795 if (shm_ebpf_cgroup.header->systemd_enabled) {
2796 if (send_cgroup_chart) {
2797 ebpf_create_systemd_socket_charts(update_every);
@@ -2732,6 +2800,9 @@ static void ebpf_socket_send_cgroup_data(int update_every)
2800 }
2801
2802 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
2803 + if (ebpf_plugin_stop())
2804 + break;
2805 +
2806 if (ect->systemd)
2807 continue;
2808
@@ -2779,8 +2850,14 @@ static void socket_collector(ebpf_module_t *em)
2850 heartbeat_t hb;
2851 heartbeat_init(&hb, USEC_PER_SEC);
2852 while (!ebpf_plugin_stop() && running_time < lifetime) {
2853 + if (ebpf_plugin_stop())
2854 + break;
2855 +
2856 heartbeat_next(&hb);
2783 - if (ebpf_plugin_stop() || ++counter != update_every)
2857 + if (ebpf_plugin_stop())
2858 + break;
2859 +
2860 + if (++counter != update_every)
2861 continue;
2862
2863 counter = 0;
@@ -2790,6 +2867,9 @@ static void socket_collector(ebpf_module_t *em)
2867 ebpf_socket_read_hash_global_tables(stats, maps_per_core);
2868 }
2869
2870 + if (ebpf_plugin_stop())
2871 + break;
2872 +
2873 netdata_mutex_lock(&lock);
2874 if (socket_global_enabled)
2875 ebpf_socket_send_data(em);
@@ -2797,6 +2877,11 @@ static void socket_collector(ebpf_module_t *em)
2877 if (socket_apps_enabled & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
2878 ebpf_socket_send_apps_data();
2879
2880 + if (ebpf_plugin_stop()) {
2881 + netdata_mutex_unlock(&lock);
2882 + break;
2883 + }
2884 +
2885 if (cgroups && shm_ebpf_cgroup.header)
2886 ebpf_socket_send_cgroup_data(update_every);
2887
@@ -2804,12 +2889,11 @@ static void socket_collector(ebpf_module_t *em)
2889
2890 netdata_mutex_unlock(&lock);
2891
2807 - netdata_mutex_lock(&ebpf_exit_cleanup);
2808 - if (running_time && !em->running_time)
2809 - running_time = update_every;
2810 - else
2811 - running_time += update_every;
2892 + if (ebpf_plugin_stop())
2893 + break;
2894
2895 + netdata_mutex_lock(&ebpf_exit_cleanup);
2896 + running_time += update_every;
2897 em->running_time = running_time;
2898 netdata_mutex_unlock(&ebpf_exit_cleanup);
2899 }
@@ -2857,8 +2941,8 @@ static void ebpf_socket_initialize_global_vectors()
2941 */
2942 static void ebpf_link_dimension_name(const char *port, uint32_t hash, const char *value)
2943 {
2860 - int test = str2i(port);
2861 - if (test < NETDATA_MINIMUM_PORT_VALUE || test > NETDATA_MAXIMUM_PORT_VALUE) {
2944 + int port_val = str2i(port);
2945 + if (port_val < NETDATA_MINIMUM_PORT_VALUE || port_val > NETDATA_MAXIMUM_PORT_VALUE) {
2946 netdata_log_error("The dimension given (%s = %s) has an invalid value and it will be ignored.", port, value);
2947 return;
2948 }
@@ -2869,7 +2953,7 @@ static void ebpf_link_dimension_name(const char *port, uint32_t hash, const char
2953 w->name = strdupz(value);
2954 w->hash = hash;
2955
2872 - w->port = (uint16_t)htons(test);
2956 + w->port = (uint16_t)htons(port_val);
2957
2958 ebpf_network_viewer_dim_name_t *names = network_viewer_opt.names;
2959 if (unlikely(!names)) {
@@ -2972,8 +3056,13 @@ static int ebpf_socket_load_bpf(ebpf_module_t *em)
3056 socket_bpf_obj = socket_bpf__open();
3057 if (!socket_bpf_obj)
3058 ret = -1;
2975 - else
3059 + else {
3060 ret = ebpf_socket_load_and_attach(socket_bpf_obj, em);
3061 + if (ret) {
3062 + socket_bpf__destroy(socket_bpf_obj);
3063 + socket_bpf_obj = NULL;
3064 + }
3065 + }
3066 }
3067 #endif
3068
@@ -2995,15 +3084,13 @@ static int ebpf_socket_load_bpf(ebpf_module_t *em)
3084 */
3085 void ebpf_socket_thread(void *ptr)
3086 {
2998 - pids_fd[NETDATA_EBPF_PIDS_SOCKET_IDX] = -1;
3087 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SOCKET_IDX, -1);
3088 ebpf_module_t *em = (ebpf_module_t *)ptr;
3089
3090 CLEANUP_FUNCTION_REGISTER(ebpf_socket_exit) cleanup_ptr = em;
3091
3003 - if (em->enabled > NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
3004 - collector_error("There is already a thread %s running", em->info.thread_name);
3005 - return;
3006 - }
3092 + if (!ebpf_module_thread_has_valid_state(em))
3093 + goto endsocket;
3094
3095 em->maps = socket_maps;
3096
src/collectors/ebpf.plugin/ebpf_socket.h
+5 -2
@@ -100,6 +100,11 @@ typedef enum ebpf_socket_idx {
100 } ebpf_socket_index_t;
101
102 #define NETDATA_SOCKET_KERNEL_FUNCTIONS "kernel"
103 +#define NETDATA_SOCKET_CHART_ORDER_BASE 21070
104 +#define NETDATA_SOCKET_CGROUP_ORDER_BASE 5300
105 +#define NETDATA_SOCKET_APPS_ORDER_BASE 20130
106 +#define NETDATA_SOCKET_SYSTEMD_ORDER_BASE 20080
107 +#define TCP_V6_CONNECT_TYPE 'T'
108 #define NETDATA_CGROUP_NET_GROUP "network"
109
110 // Global chart name
@@ -286,12 +291,10 @@ typedef struct netdata_socket_idx {
291 uint32_t pid;
292 } netdata_socket_idx_t;
293
289 -void ebpf_clean_port_structure(ebpf_network_viewer_port_list_t **clean);
294 extern ebpf_network_viewer_port_list_t *listen_ports;
295 void update_listen_table(uint16_t value, uint16_t proto, netdata_passive_connection_t *values);
296 void ebpf_fill_ip_list_unsafe(ebpf_network_viewer_ip_list_t **out, ebpf_network_viewer_ip_list_t *in, char *table);
297 void ebpf_parse_service_name_section(struct config *cfg);
294 -void ebpf_parse_ips_unsafe(const char *ptr);
298 void ebpf_parse_ports(const char *ptr);
299 void ebpf_socket_read_open_connections(BUFFER *buf, struct ebpf_module *em);
300 void ebpf_socket_fill_publish_apps(ebpf_socket_publish_apps_t *curr, netdata_socket_t *ns);
src/collectors/ebpf.plugin/ebpf_socket_ipc.c
+1 -1
@@ -31,7 +31,7 @@ static int ebpf_ipc_snd_callback(POLLINFO *pi __maybe_unused, nd_poll_event_t *e
31
32 static bool ebpf_ipc_should_stop(void)
33 {
34 - return false;
34 + return ebpf_plugin_stop();
35 }
36
37 void ebpf_socket_thread_ipc(void *ptr)
src/collectors/ebpf.plugin/ebpf_softirq.c
+34 -40
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_softirq.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 struct config softirq_config = APPCONFIG_INITIALIZER;
8
@@ -16,16 +17,7 @@ static ebpf_local_maps_t softirq_maps[] = {
17 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
18 #endif
19 },
19 - /* end */
20 - {.name = NULL,
21 - .internal_input = 0,
22 - .user_input = 0,
23 - .type = NETDATA_EBPF_MAP_CONTROLLER,
24 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
25 -#ifdef LIBBPF_MAJOR_VERSION
26 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
27 -#endif
28 - }};
20 + {.name = NULL, .internal_input = 0, .user_input = 0}};
21
22 #define SOFTIRQ_TP_CLASS_IRQ "irq"
23 static ebpf_tracepoint_t softirq_tracepoints[] = {
@@ -51,6 +43,7 @@ static softirq_val_t softirq_vals[] = {
43
44 // tmp store for soft IRQ values we get from a per-CPU eBPF map.
45 static softirq_ebpf_val_t *softirq_ebpf_vals = NULL;
46 +static bool softirq_safe_clean = false;
47
48 /**
49 * Obsolete global
@@ -74,20 +67,20 @@ static void ebpf_obsolete_softirq_global(ebpf_module_t *em)
67 em->update_every);
68 }
69
77 -/**
78 - * Cleanup
79 - *
80 - * Clean up allocated memory.
81 - *
82 - * @param ptr thread data.
83 - */
70 static void softirq_cleanup(void *pptr)
71 {
72 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
73 if (!em)
74 return;
75
90 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
76 + if (!softirq_safe_clean) {
77 + netdata_mutex_lock(&ebpf_exit_cleanup);
78 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
79 + netdata_mutex_unlock(&ebpf_exit_cleanup);
80 + return;
81 + }
82 +
83 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
84 netdata_mutex_lock(&lock);
85
86 ebpf_obsolete_softirq_global(em);
@@ -96,23 +89,17 @@ static void softirq_cleanup(void *pptr)
89 fflush(stdout);
90 }
91
99 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
100 -
101 - if (em->objects) {
102 - ebpf_unload_legacy_code(em->objects, em->probe_links);
103 - em->objects = NULL;
104 - em->probe_links = NULL;
105 - }
106 -
92 for (int i = 0; softirq_tracepoints[i].class != NULL; i++) {
93 ebpf_disable_tracepoint(&softirq_tracepoints[i]);
94 }
95 freez(softirq_ebpf_vals);
96 softirq_ebpf_vals = NULL;
97
98 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
99 + em->functions.bpf_unload(em);
100 +
101 netdata_mutex_lock(&ebpf_exit_cleanup);
102 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
115 - ebpf_update_stats(&plugin_statistics, em);
103 netdata_mutex_unlock(&ebpf_exit_cleanup);
104 }
105
@@ -131,25 +118,22 @@ static void softirq_read_latency_map(int maps_per_core)
118 {
119 int fd = softirq_maps[SOFTIRQ_MAP_LATENCY].map_fd;
120 int i;
134 - size_t length = sizeof(softirq_ebpf_val_t);
135 - if (maps_per_core)
136 - length *= ebpf_nprocs;
121 + int end = (maps_per_core) ? ebpf_nprocs : 1;
122
123 for (i = 0; i < NETDATA_SOFTIRQ_MAX_IRQS; i++) {
139 - int test = bpf_map_lookup_elem(fd, &i, softirq_ebpf_vals);
140 - if (unlikely(test < 0)) {
124 + int ret = bpf_map_lookup_elem(fd, &i, softirq_ebpf_vals);
125 + if (unlikely(ret < 0)) {
126 continue;
127 }
128
129 uint64_t total_latency = 0;
130 int cpu_i;
146 - int end = (maps_per_core) ? ebpf_nprocs : 1;
131 for (cpu_i = 0; cpu_i < end; cpu_i++) {
132 total_latency += softirq_ebpf_vals[cpu_i].latency / 1000;
133 }
134
135 softirq_vals[i].latency = total_latency;
152 - memset(softirq_ebpf_vals, 0, length);
136 + memset(softirq_ebpf_vals, 0, end * sizeof(softirq_ebpf_val_t));
137 }
138 }
139
@@ -215,8 +199,14 @@ static void softirq_collector(ebpf_module_t *em)
199 uint32_t running_time = 0;
200 uint32_t lifetime = em->lifetime;
201 while (!ebpf_plugin_stop() && running_time < lifetime) {
202 + if (ebpf_plugin_stop())
203 + break;
204 +
205 heartbeat_next(&hb);
219 - if (ebpf_plugin_stop() || ++counter != update_every)
206 + if (ebpf_plugin_stop())
207 + break;
208 +
209 + if (++counter != update_every)
210 continue;
211
212 counter = 0;
@@ -230,12 +220,11 @@ static void softirq_collector(ebpf_module_t *em)
220
221 netdata_mutex_unlock(&lock);
222
233 - netdata_mutex_lock(&ebpf_exit_cleanup);
234 - if (running_time && !em->running_time)
235 - running_time = update_every;
236 - else
237 - running_time += update_every;
223 + if (ebpf_plugin_stop())
224 + break;
225
226 + netdata_mutex_lock(&ebpf_exit_cleanup);
227 + running_time += update_every;
228 em->running_time = running_time;
229 netdata_mutex_unlock(&ebpf_exit_cleanup);
230 }
@@ -257,6 +246,10 @@ void ebpf_softirq_thread(void *ptr)
246
247 CLEANUP_FUNCTION_REGISTER(softirq_cleanup) cleanup_ptr = em;
248
249 + if (!ebpf_module_thread_has_valid_state(em)) {
250 + goto endsoftirq;
251 + }
252 +
253 em->maps = softirq_maps;
254
255 if (ebpf_enable_tracepoints(softirq_tracepoints) == 0) {
@@ -271,6 +264,7 @@ void ebpf_softirq_thread(void *ptr)
264 goto endsoftirq;
265 }
266
267 + softirq_safe_clean = true;
268 softirq_collector(em);
269
270 endsoftirq:
src/collectors/ebpf.plugin/ebpf_softirq.h
+1 -1
@@ -25,7 +25,7 @@ typedef struct softirq_ebpf_val {
25 #define NETDATA_EBPF_MODULE_NAME_SOFTIRQ "softirq"
26 #define NETDATA_SOFTIRQ_CONFIG_FILE "softirq.conf"
27
28 -typedef struct sofirq_val {
28 +typedef struct softirq_val {
29 uint64_t latency;
30 char *name;
31 } softirq_val_t;
src/collectors/ebpf.plugin/ebpf_swap.c
+205 -112
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_swap.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static char *swap_dimension_name[NETDATA_SWAP_END] = {"read", "write"};
8 static netdata_syscall_stat_t swap_aggregated_data[NETDATA_SWAP_END];
@@ -42,13 +43,7 @@ static ebpf_local_maps_t swap_maps[] = {
43 .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
44 #endif
45 },
45 - {.name = NULL,
46 - .internal_input = 0,
47 - .user_input = 0,
48 -#ifdef LIBBPF_MAJOR_VERSION
49 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
50 -#endif
51 - }};
46 + {.name = NULL, .internal_input = 0, .user_input = 0}};
47
48 netdata_ebpf_targets_t swap_targets[] = {
49 {.name = NULL, .mode = EBPF_LOAD_TRAMPOLINE},
@@ -57,6 +52,7 @@ netdata_ebpf_targets_t swap_targets[] = {
52
53 #define NETDATA_SWAP_KEY_WRITE_START (2)
54 static char *swap_functions[] = {"swap_readpage", "swap_read_folio", "swap_writepage", "__swap_writepage", NULL};
55 +static bool swap_safe_clean = false;
56
57 struct netdata_static_thread ebpf_read_swap = {
58 .name = "EBPF_READ_SWAP",
@@ -299,29 +295,24 @@ static void ebpf_obsolete_specific_swap_charts(char *type, int update_every);
295 */
296 static void ebpf_obsolete_swap_services(ebpf_module_t *em, char *id)
297 {
302 - ebpf_write_chart_obsolete(
303 - id,
304 - NETDATA_MEM_SWAP_READ_CHART,
305 - "",
306 - "Calls to function swap_readpage.",
307 - EBPF_COMMON_UNITS_CALLS_PER_SEC,
308 - NETDATA_SYSTEM_SWAP_SUBMENU,
309 - NETDATA_EBPF_CHART_TYPE_LINE,
310 - NETDATA_SYSTEMD_SWAP_READ_CONTEXT,
311 - 20191,
312 - em->update_every);
298 + static const char *charts[] = {NETDATA_MEM_SWAP_READ_CHART, NETDATA_MEM_SWAP_WRITE_CHART};
299 + static const char *contexts[] = {NETDATA_SYSTEMD_SWAP_READ_CONTEXT, NETDATA_CGROUP_SWAP_WRITE_CONTEXT};
300 + static const uint32_t orders[] = {20191, 20192};
301
314 - ebpf_write_chart_obsolete(
315 - id,
316 - NETDATA_MEM_SWAP_WRITE_CHART,
317 - "",
318 - "Calls to function swap_writepage.",
319 - EBPF_COMMON_UNITS_CALLS_PER_SEC,
320 - NETDATA_SYSTEM_SWAP_SUBMENU,
321 - NETDATA_EBPF_CHART_TYPE_LINE,
322 - NETDATA_CGROUP_SWAP_WRITE_CONTEXT,
323 - 20192,
324 - em->update_every);
302 + int i;
303 + for (i = 0; i < NETDATA_SWAP_END; i++) {
304 + ebpf_write_chart_obsolete(
305 + id,
306 + charts[i],
307 + "",
308 + (i == 0) ? "Calls to function swap_readpage." : "Calls to function swap_writepage.",
309 + EBPF_COMMON_UNITS_CALLS_PER_SEC,
310 + NETDATA_SYSTEM_SWAP_SUBMENU,
311 + NETDATA_EBPF_CHART_TYPE_LINE,
312 + contexts[i],
313 + orders[i],
314 + em->update_every);
315 + }
316 }
317
318 /**
@@ -349,7 +340,7 @@ static inline void ebpf_obsolete_swap_cgroup_charts(ebpf_module_t *em)
340 }
341
342 /**
352 - * Obsolette apps charts
343 + * Obsolete apps charts
344 *
345 * Obsolete apps charts.
346 *
@@ -421,19 +412,47 @@ static void ebpf_obsolete_swap_global(ebpf_module_t *em)
412 *
413 * @param ptr thread data.
414 */
424 -static void ebpf_swap_exit(void *ptr)
415 +void ebpf_swap_unload_bpf(ebpf_module_t *em)
416 {
426 - pids_fd[NETDATA_EBPF_PIDS_SWAP_IDX] = -1;
427 - ebpf_module_t *em = (ebpf_module_t *)ptr;
417 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
418 + if ((uintptr_t)em->objects < 4096) {
419 + netdata_log_error(
420 + "Invalid em->objects pointer (0x%lx) detected during swap cleanup, skipping bpf_object__close",
421 + (unsigned long)em->objects);
422 + freez(em->probe_links);
423 + } else {
424 + if (em->objects && em->probe_links)
425 + ebpf_unload_legacy_code(em->objects, em->probe_links);
426 + }
427 + em->objects = NULL;
428 + em->probe_links = NULL;
429 + }
430 +#ifdef LIBBPF_MAJOR_VERSION
431 + else if (swap_bpf_obj) {
432 + swap_bpf__destroy(swap_bpf_obj);
433 + swap_bpf_obj = NULL;
434 + }
435 +#endif
436 +}
437 +
438 +static void ebpf_swap_exit(void *pptr)
439 +{
440 + ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
441 + if (!em)
442 + return;
443 +
444 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SWAP_IDX, -1);
445
446 netdata_mutex_lock(&lock);
447 collect_pids &= ~(1 << EBPF_MODULE_SWAP_IDX);
448 netdata_mutex_unlock(&lock);
449
433 - if (ebpf_read_swap.thread)
450 + if (ebpf_read_swap.thread) {
451 nd_thread_signal_cancel(ebpf_read_swap.thread);
452 + nd_thread_join(ebpf_read_swap.thread);
453 + }
454
436 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
455 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
456 netdata_mutex_lock(&lock);
457 if (em->cgroup_charts) {
458 ebpf_obsolete_swap_cgroup_charts(em);
@@ -450,23 +469,23 @@ static void ebpf_swap_exit(void *ptr)
469 netdata_mutex_unlock(&lock);
470 }
471
453 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
454 -
455 -#ifdef LIBBPF_MAJOR_VERSION
456 - if (bpf_obj) {
457 - swap_bpf__destroy(bpf_obj);
458 - bpf_obj = NULL;
459 - }
460 -#endif
461 - if (em->objects) {
462 - ebpf_unload_legacy_code(em->objects, em->probe_links);
463 - em->objects = NULL;
464 - em->probe_links = NULL;
472 + if (!swap_safe_clean) {
473 + netdata_mutex_lock(&ebpf_exit_cleanup);
474 + em->enabled = NETDATA_THREAD_EBPF_STOPPED;
475 + netdata_mutex_unlock(&ebpf_exit_cleanup);
476 + return;
477 }
478
479 + freez(swap_vector);
480 + swap_vector = NULL;
481 + freez(swap_values);
482 + swap_values = NULL;
483 +
484 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
485 + em->functions.bpf_unload(em);
486 +
487 netdata_mutex_lock(&ebpf_exit_cleanup);
488 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
469 - ebpf_update_stats(&plugin_statistics, em);
489 netdata_mutex_unlock(&ebpf_exit_cleanup);
490 }
491
@@ -488,15 +507,14 @@ static void swap_apps_accumulator(netdata_ebpf_swap_t *out, int maps_per_core)
507 {
508 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
509 netdata_ebpf_swap_t *total = &out[0];
491 - uint64_t ct = total->ct;
510 for (i = 1; i < end; i++) {
511 + if (ebpf_plugin_stop())
512 + break;
513 +
514 netdata_ebpf_swap_t *w = &out[i];
515 total->write += w->write;
516 total->read += w->read;
517
497 - if (w->ct > ct)
498 - ct = w->ct;
499 -
518 if (!total->name[0] && w->name[0])
519 strncpyz(total->name, w->name, sizeof(total->name) - 1);
520 }
@@ -507,11 +525,14 @@ static void swap_apps_accumulator(netdata_ebpf_swap_t *out, int maps_per_core)
525 *
526 * Update cgroup data based in
527 */
510 -static void ebpf_update_swap_cgroup()
528 +static void ebpf_update_swap_cgroup(void)
529 {
530 ebpf_cgroup_target_t *ect;
531 netdata_mutex_lock(&mutex_cgroup_shm);
532 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
533 + if (ebpf_plugin_stop())
534 + break;
535 +
536 struct pid_on_target2 *pids;
537 for (pids = ect->pids; pids; pids = pids->next) {
538 uint32_t pid = pids->pid;
@@ -559,11 +580,14 @@ static void ebpf_swap_sum_pids(netdata_publish_swap_t *swap, struct ebpf_pid_on_
580 /**
581 * Resume apps data
582 */
562 -void ebpf_swap_resume_apps_data()
583 +void ebpf_swap_resume_apps_data(void)
584 {
585 struct ebpf_target *w;
586 netdata_mutex_lock(&collect_data_mutex);
587 for (w = apps_groups_root_target; w; w = w->next) {
588 + if (ebpf_plugin_stop())
589 + break;
590 +
591 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SWAP_IDX))))
592 continue;
593
@@ -589,6 +613,9 @@ static void ebpf_read_swap_apps_table(int maps_per_core)
613
614 uint32_t key = 0, next_key = 0;
615 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
616 + if (ebpf_plugin_stop())
617 + break;
618 +
619 if (bpf_map_lookup_elem(fd, &key, cv)) {
620 goto end_swap_loop;
621 }
@@ -640,31 +667,49 @@ void ebpf_read_swap_thread(void *ptr)
667 uint32_t lifetime = em->lifetime;
668 uint32_t running_time = 0;
669 int cgroups = em->cgroup_charts;
643 - pids_fd[NETDATA_EBPF_PIDS_SWAP_IDX] = swap_maps[NETDATA_PID_SWAP_TABLE].map_fd;
670 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SWAP_IDX, swap_maps[NETDATA_PID_SWAP_TABLE].map_fd);
671
672 heartbeat_t hb;
646 - heartbeat_init(&hb, update_every * USEC_PER_SEC);
673 + heartbeat_init(&hb, USEC_PER_SEC);
674 while (!ebpf_plugin_stop() && running_time < lifetime) {
675 + if (ebpf_plugin_stop())
676 + break;
677 +
678 heartbeat_next(&hb);
649 - if (ebpf_plugin_stop() || ++counter != update_every)
679 + if (ebpf_plugin_stop())
680 + break;
681 +
682 + if (++counter != update_every)
683 continue;
684
652 - sem_wait(shm_mutex_ebpf_integration);
685 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
686 + if (errno != ECANCELED)
687 + netdata_log_error("SWAP: Failed to wait on semaphore.");
688 + break;
689 + }
690 ebpf_read_swap_apps_table(maps_per_core);
691 ebpf_swap_resume_apps_data();
692 + if (ebpf_plugin_stop()) {
693 + if (sem_post(shm_mutex_ebpf_integration))
694 + netdata_log_error("SWAP: Failed to post semaphore.");
695 + break;
696 + }
697 +
698 if (cgroups && shm_ebpf_cgroup.header)
699 ebpf_update_swap_cgroup();
700
658 - sem_post(shm_mutex_ebpf_integration);
701 + if (sem_post(shm_mutex_ebpf_integration)) {
702 + netdata_log_error("SWAP: Failed to post semaphore.");
703 + break;
704 + }
705
706 counter = 0;
707
662 - netdata_mutex_lock(&ebpf_exit_cleanup);
663 - if (running_time && !em->running_time)
664 - running_time = update_every;
665 - else
666 - running_time += update_every;
708 + if (ebpf_plugin_stop())
709 + break;
710
711 + netdata_mutex_lock(&ebpf_exit_cleanup);
712 + running_time += update_every;
713 em->running_time = running_time;
714 netdata_mutex_unlock(&ebpf_exit_cleanup);
715 }
@@ -675,7 +720,7 @@ void ebpf_read_swap_thread(void *ptr)
720 *
721 * Send global charts to Netdata
722 */
678 -static void swap_send_global()
723 +static void swap_send_global(void)
724 {
725 write_io_chart(
726 NETDATA_MEM_SWAP_CHART,
@@ -723,6 +768,9 @@ void ebpf_swap_send_apps_data(struct ebpf_target *root)
768 struct ebpf_target *w;
769 netdata_mutex_lock(&collect_data_mutex);
770 for (w = root; w; w = w->next) {
771 + if (ebpf_plugin_stop())
772 + break;
773 +
774 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_SWAP_IDX))))
775 continue;
776
@@ -763,26 +811,42 @@ static void ebpf_swap_sum_cgroup_pids(netdata_publish_swap_t *swap, struct pid_o
811 swap->read = (local_read >= swap->read) ? local_read : swap->read;
812 }
813
814 +/**
815 + * Send swap chart dimension
816 + *
817 + * Send a single swap chart dimension.
818 + *
819 + * @param type The chart type (cgroup/systemd name)
820 + * @param chart The chart name
821 + * @param value The value to send
822 + */
823 +static void swap_send_dimension(const char *type, const char *chart, uint64_t value)
824 +{
825 + ebpf_write_begin_chart(type, chart, "");
826 + write_chart_dimension("calls", (long long)value);
827 + ebpf_write_end_chart();
828 +}
829 +
830 /**
831 * Send Systemd charts
832 *
833 * Send collected data to Netdata.
834 */
771 -static void ebpf_send_systemd_swap_charts()
835 +static void ebpf_send_systemd_swap_charts(void)
836 {
837 + static const char *charts[] = {NETDATA_MEM_SWAP_READ_CHART, NETDATA_MEM_SWAP_WRITE_CHART};
838 +
839 ebpf_cgroup_target_t *ect;
840 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
841 + if (ebpf_plugin_stop())
842 + break;
843 +
844 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_SWAP_CHART))) {
845 continue;
846 }
847
779 - ebpf_write_begin_chart(ect->name, NETDATA_MEM_SWAP_READ_CHART, "");
780 - write_chart_dimension("calls", (long long)ect->publish_systemd_swap.read);
781 - ebpf_write_end_chart();
782 -
783 - ebpf_write_begin_chart(ect->name, NETDATA_MEM_SWAP_WRITE_CHART, "");
784 - write_chart_dimension("calls", (long long)ect->publish_systemd_swap.write);
785 - ebpf_write_end_chart();
848 + swap_send_dimension(ect->name, charts[0], ect->publish_systemd_swap.read);
849 + swap_send_dimension(ect->name, charts[1], ect->publish_systemd_swap.write);
850 }
851 }
852
@@ -842,29 +906,24 @@ static void ebpf_create_specific_swap_charts(char *type, int update_every)
906 */
907 static void ebpf_obsolete_specific_swap_charts(char *type, int update_every)
908 {
845 - ebpf_write_chart_obsolete(
846 - type,
847 - NETDATA_MEM_SWAP_READ_CHART,
848 - "",
849 - "Calls to function swap_readpage.",
850 - EBPF_COMMON_UNITS_CALLS_PER_SEC,
851 - NETDATA_SYSTEM_SWAP_SUBMENU,
852 - NETDATA_EBPF_CHART_TYPE_LINE,
853 - NETDATA_CGROUP_SWAP_READ_CONTEXT,
854 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5100,
855 - update_every);
909 + static const char *charts[] = {NETDATA_MEM_SWAP_READ_CHART, NETDATA_MEM_SWAP_WRITE_CHART};
910 + static const char *contexts[] = {NETDATA_CGROUP_SWAP_READ_CONTEXT, NETDATA_CGROUP_SWAP_WRITE_CONTEXT};
911 + static const uint32_t offsets[] = {0, 1};
912
857 - ebpf_write_chart_obsolete(
858 - type,
859 - NETDATA_MEM_SWAP_WRITE_CHART,
860 - "",
861 - "Calls to function swap_writepage.",
862 - EBPF_COMMON_UNITS_CALLS_PER_SEC,
863 - NETDATA_SYSTEM_SWAP_SUBMENU,
864 - NETDATA_EBPF_CHART_TYPE_LINE,
865 - NETDATA_CGROUP_SWAP_WRITE_CONTEXT,
866 - NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5101,
867 - update_every);
913 + int i;
914 + for (i = 0; i < NETDATA_SWAP_END; i++) {
915 + ebpf_write_chart_obsolete(
916 + type,
917 + charts[i],
918 + "",
919 + (i == 0) ? "Calls to function swap_readpage." : "Calls to function swap_writepage.",
920 + EBPF_COMMON_UNITS_CALLS_PER_SEC,
921 + NETDATA_SYSTEM_SWAP_SUBMENU,
922 + NETDATA_EBPF_CHART_TYPE_LINE,
923 + contexts[i],
924 + NETDATA_CHART_PRIO_CGROUPS_CONTAINERS + 5100 + offsets[i],
925 + update_every);
926 + }
927 }
928
929 /*
@@ -926,6 +985,9 @@ static void ebpf_create_systemd_swap_charts(int update_every)
985
986 ebpf_cgroup_target_t *w;
987 for (w = ebpf_cgroup_pids; w; w = w->next) {
988 + if (ebpf_plugin_stop())
989 + break;
990 +
991 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_SWAP_CHART))
992 continue;
993
@@ -951,6 +1013,11 @@ void ebpf_swap_send_cgroup_data(int update_every)
1013 ebpf_swap_sum_cgroup_pids(&ect->publish_systemd_swap, ect->pids);
1014 }
1015
1016 + if (ebpf_plugin_stop()) {
1017 + netdata_mutex_unlock(&mutex_cgroup_shm);
1018 + return;
1019 + }
1020 +
1021 if (shm_ebpf_cgroup.header->systemd_enabled) {
1022 if (send_cgroup_chart) {
1023 ebpf_create_systemd_swap_charts(update_every);
@@ -960,6 +1027,9 @@ void ebpf_swap_send_cgroup_data(int update_every)
1027 }
1028
1029 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1030 + if (ebpf_plugin_stop())
1031 + break;
1032 +
1033 if (ect->systemd)
1034 continue;
1035
@@ -998,8 +1068,14 @@ static void swap_collector(ebpf_module_t *em)
1068 heartbeat_t hb;
1069 heartbeat_init(&hb, USEC_PER_SEC);
1070 while (!ebpf_plugin_stop() && running_time < lifetime) {
1071 + if (ebpf_plugin_stop())
1072 + break;
1073 +
1074 (void)heartbeat_next(&hb);
1002 - if (ebpf_plugin_stop() || ++counter != update_every)
1075 + if (ebpf_plugin_stop())
1076 + break;
1077 +
1078 + if (++counter != update_every)
1079 continue;
1080
1081 counter = 0;
@@ -1013,17 +1089,21 @@ static void swap_collector(ebpf_module_t *em)
1089 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
1090 ebpf_swap_send_apps_data(apps_groups_root_target);
1091
1092 + if (ebpf_plugin_stop()) {
1093 + netdata_mutex_unlock(&lock);
1094 + break;
1095 + }
1096 +
1097 if (cgroup && shm_ebpf_cgroup.header)
1098 ebpf_swap_send_cgroup_data(update_every);
1099
1100 netdata_mutex_unlock(&lock);
1101
1021 - netdata_mutex_lock(&ebpf_exit_cleanup);
1022 - if (running_time && !em->running_time)
1023 - running_time = update_every;
1024 - else
1025 - running_time += update_every;
1102 + if (ebpf_plugin_stop())
1103 + break;
1104
1105 + netdata_mutex_lock(&ebpf_exit_cleanup);
1106 + running_time += update_every;
1107 em->running_time = running_time;
1108 netdata_mutex_unlock(&ebpf_exit_cleanup);
1109 }
@@ -1094,7 +1174,7 @@ void ebpf_swap_create_apps_charts(struct ebpf_module *em, void *ptr)
1174 * We are not testing the return, because callocz does this and shutdown the software
1175 * case it was not possible to allocate.
1176 */
1097 -static void ebpf_swap_allocate_global_vectors()
1177 +static void ebpf_swap_allocate_global_vectors(void)
1178 {
1179 swap_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_ebpf_swap_t));
1180
@@ -1159,11 +1239,16 @@ static int ebpf_swap_load_bpf(ebpf_module_t *em)
1239 }
1240 #ifdef LIBBPF_MAJOR_VERSION
1241 else {
1162 - bpf_obj = swap_bpf__open();
1163 - if (!bpf_obj)
1242 + swap_bpf_obj = swap_bpf__open();
1243 + if (!swap_bpf_obj)
1244 ret = -1;
1165 - else
1166 - ret = ebpf_swap_load_and_attach(bpf_obj, em);
1245 + else {
1246 + ret = ebpf_swap_load_and_attach(swap_bpf_obj, em);
1247 + if (ret) {
1248 + swap_bpf__destroy(swap_bpf_obj);
1249 + swap_bpf_obj = NULL;
1250 + }
1251 + }
1252 }
1253 #endif
1254
@@ -1180,24 +1265,26 @@ static int ebpf_swap_load_bpf(ebpf_module_t *em)
1265 *
1266 * @return It returns 0 when one of the functions is present and -1 otherwise.
1267 */
1183 -static int ebpf_swap_set_internal_value()
1268 +static int ebpf_swap_set_internal_value(void)
1269 {
1185 - ebpf_addresses_t address = {.function = NULL, .hash = 0, .addr = 0};
1270 + ebpf_addresses_t address = {.function = NULL, .hash = 0, .addr = 0, .type = 0};
1271 int i;
1272 for (i = 0; swap_functions[i]; i++) {
1273 address.function = swap_functions[i];
1274 ebpf_load_addresses(&address, -1);
1275 if (address.addr) {
1191 - int key = (i < 2) ? NETDATA_KEY_SWAP_READPAGE_CALL: NETDATA_KEY_SWAP_WRITEPAGE_CALL;
1276 + int key = (i < 2) ? NETDATA_KEY_SWAP_READPAGE_CALL : NETDATA_KEY_SWAP_WRITEPAGE_CALL;
1277 swap_targets[key].name = address.function;
1278 address.addr = 0;
1279 }
1280 }
1281
1282 if (!swap_targets[NETDATA_KEY_SWAP_READPAGE_CALL].name || !swap_targets[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name) {
1198 - netdata_log_error("%s (%s, %s) swap.", NETDATA_EBPF_DEFAULT_FNT_NOT_FOUND,
1199 - swap_targets[NETDATA_KEY_SWAP_READPAGE_CALL].name,
1200 - swap_targets[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name);
1283 + netdata_log_error(
1284 + "%s (%s, %s) swap.",
1285 + NETDATA_EBPF_DEFAULT_FNT_NOT_FOUND,
1286 + swap_targets[NETDATA_KEY_SWAP_READPAGE_CALL].name,
1287 + swap_targets[NETDATA_KEY_SWAP_WRITEPAGE_CALL].name);
1288 return -1;
1289 }
1290
@@ -1215,10 +1302,15 @@ static int ebpf_swap_set_internal_value()
1302 */
1303 void ebpf_swap_thread(void *ptr)
1304 {
1305 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_SWAP_IDX, -1);
1306 ebpf_module_t *em = (ebpf_module_t *)ptr;
1307
1308 CLEANUP_FUNCTION_REGISTER(ebpf_swap_exit) cleanup_ptr = em;
1309
1310 + if (!ebpf_module_thread_has_valid_state(em)) {
1311 + goto endswap;
1312 + }
1313 +
1314 em->maps = swap_maps;
1315
1316 ebpf_update_pid_table(&swap_maps[NETDATA_PID_SWAP_TABLE], em);
@@ -1254,6 +1346,7 @@ void ebpf_swap_thread(void *ptr)
1346 ebpf_read_swap.thread =
1347 nd_thread_create(ebpf_read_swap.name, NETDATA_THREAD_OPTION_DEFAULT, ebpf_read_swap_thread, em);
1348
1349 + swap_safe_clean = true;
1350 swap_collector(em);
1351
1352 endswap:
src/collectors/ebpf.plugin/ebpf_sync.c
+82 -167
@@ -2,6 +2,7 @@
2
3 #include "ebpf.h"
4 #include "ebpf_sync.h"
5 +#include "libbpf_api/ebpf_library.h"
6
7 static char *sync_counter_dimension_name[NETDATA_SYNC_IDX_END] =
8 {"sync", "syncfs", "msync", "fsync", "fdatasync", "sync_file_range"};
@@ -10,125 +11,36 @@ static netdata_publish_syscall_t sync_counter_publish_aggregated[NETDATA_SYNC_ID
11
12 static netdata_idx_t sync_hash_values[NETDATA_SYNC_IDX_END];
13
13 -ebpf_local_maps_t sync_maps[] = {
14 - {.name = "tbl_sync",
15 - .internal_input = NETDATA_SYNC_END,
16 - .user_input = 0,
17 - .type = NETDATA_EBPF_MAP_STATIC,
18 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
19 -#ifdef LIBBPF_MAJOR_VERSION
20 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
21 -#endif
22 - },
23 - {.name = NULL,
24 - .internal_input = 0,
25 - .user_input = 0,
26 - .type = NETDATA_EBPF_MAP_CONTROLLER,
27 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
28 -#ifdef LIBBPF_MAJOR_VERSION
29 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
30 -#endif
31 - }};
32 -
33 -ebpf_local_maps_t syncfs_maps[] = {
34 - {.name = "tbl_syncfs",
35 - .internal_input = NETDATA_SYNC_END,
36 - .user_input = 0,
37 - .type = NETDATA_EBPF_MAP_STATIC,
38 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
39 -#ifdef LIBBPF_MAJOR_VERSION
40 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
41 -#endif
42 - },
43 - {.name = NULL,
44 - .internal_input = 0,
45 - .user_input = 0,
46 - .type = NETDATA_EBPF_MAP_CONTROLLER,
47 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
48 -#ifdef LIBBPF_MAJOR_VERSION
49 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
50 -#endif
51 - }};
52 -
53 -ebpf_local_maps_t msync_maps[] = {
54 - {.name = "tbl_msync",
55 - .internal_input = NETDATA_SYNC_END,
56 - .user_input = 0,
57 - .type = NETDATA_EBPF_MAP_STATIC,
58 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
59 -#ifdef LIBBPF_MAJOR_VERSION
60 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
61 -#endif
62 - },
63 - {.name = NULL,
64 - .internal_input = 0,
65 - .user_input = 0,
66 - .type = NETDATA_EBPF_MAP_CONTROLLER,
67 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
68 -#ifdef LIBBPF_MAJOR_VERSION
69 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
70 -#endif
71 - }};
72 -
73 -ebpf_local_maps_t fsync_maps[] = {
74 - {.name = "tbl_fsync",
75 - .internal_input = NETDATA_SYNC_END,
76 - .user_input = 0,
77 - .type = NETDATA_EBPF_MAP_STATIC,
78 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
79 -#ifdef LIBBPF_MAJOR_VERSION
80 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
81 -#endif
82 - },
83 - {.name = NULL,
84 - .internal_input = 0,
85 - .user_input = 0,
86 - .type = NETDATA_EBPF_MAP_CONTROLLER,
87 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
88 -#ifdef LIBBPF_MAJOR_VERSION
89 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
90 -#endif
91 - }};
92 -
93 -ebpf_local_maps_t fdatasync_maps[] = {
94 - {.name = "tbl_fdatasync",
95 - .internal_input = NETDATA_SYNC_END,
96 - .user_input = 0,
97 - .type = NETDATA_EBPF_MAP_STATIC,
98 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
99 -#ifdef LIBBPF_MAJOR_VERSION
100 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
101 -#endif
102 - },
103 - {.name = NULL,
104 - .internal_input = 0,
105 - .user_input = 0,
106 - .type = NETDATA_EBPF_MAP_CONTROLLER,
107 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
108 -#ifdef LIBBPF_MAJOR_VERSION
109 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
110 -#endif
111 - }};
112 -
113 -ebpf_local_maps_t sync_file_range_maps[] = {
114 - {.name = "tbl_syncfr",
115 - .internal_input = NETDATA_SYNC_END,
116 - .user_input = 0,
117 - .type = NETDATA_EBPF_MAP_STATIC,
118 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
14 +static ebpf_local_maps_t sync_maps[NETDATA_SYNC_IDX_END][2];
15 +
16 +static void ebpf_initialize_sync_maps(void)
17 +{
18 + static const char *map_names[NETDATA_SYNC_IDX_END] = {
19 + "tbl_sync", "tbl_syncfs", "tbl_msync", "tbl_fsync", "tbl_fdatasync", "tbl_syncfr"};
20 +
21 + for (int i = 0; i < NETDATA_SYNC_IDX_END; i++) {
22 + sync_maps[i][0] = (ebpf_local_maps_t){
23 + .name = map_names[i],
24 + .internal_input = NETDATA_SYNC_END,
25 + .user_input = 0,
26 + .type = NETDATA_EBPF_MAP_STATIC,
27 + .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
28 #ifdef LIBBPF_MAJOR_VERSION
120 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
29 + .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
30 #endif
122 - },
123 - {.name = NULL,
124 - .internal_input = 0,
125 - .user_input = 0,
126 - .type = NETDATA_EBPF_MAP_CONTROLLER,
127 - .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
31 + };
32 + sync_maps[i][1] = (ebpf_local_maps_t){
33 + .name = NULL,
34 + .internal_input = 0,
35 + .user_input = 0,
36 + .type = NETDATA_EBPF_MAP_CONTROLLER,
37 + .map_fd = ND_EBPF_MAP_FD_NOT_INITIALIZED,
38 #ifdef LIBBPF_MAJOR_VERSION
129 - .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
39 + .map_type = BPF_MAP_TYPE_PERCPU_ARRAY
40 #endif
131 - }};
41 + };
42 + }
43 +}
44
45 struct config sync_config = APPCONFIG_INITIALIZER;
46
@@ -230,14 +142,14 @@ static inline int
142 ebpf_sync_load_and_attach(struct sync_bpf *obj, ebpf_module_t *em, char *target, sync_syscalls_index_t idx)
143 {
144 netdata_ebpf_targets_t *synct = em->targets;
233 - netdata_ebpf_program_loaded_t test = synct[NETDATA_SYNC_SYNC_IDX].mode;
145 + netdata_ebpf_program_loaded_t mode = synct[NETDATA_SYNC_SYNC_IDX].mode;
146
235 - if (test == EBPF_LOAD_TRAMPOLINE) {
147 + if (mode == EBPF_LOAD_TRAMPOLINE) {
148 ebpf_sync_disable_probe(obj);
149 ebpf_sync_disable_tracepoints(obj, NETDATA_SYNC_IDX_END);
150
151 bpf_program__set_attach_target(obj->progs.netdata_sync_fentry, 0, target);
240 - } else if (test == EBPF_LOAD_PROBE || test == EBPF_LOAD_RETPROBE) {
152 + } else if (mode == EBPF_LOAD_PROBE || mode == EBPF_LOAD_RETPROBE) {
153 ebpf_sync_disable_tracepoints(obj, NETDATA_SYNC_IDX_END);
154 ebpf_sync_disable_trampoline(obj);
155 } else {
@@ -251,7 +163,7 @@ ebpf_sync_load_and_attach(struct sync_bpf *obj, ebpf_module_t *em, char *target,
163
164 int ret = sync_bpf__load(obj);
165 if (!ret) {
254 - if (test != EBPF_LOAD_PROBE && test != EBPF_LOAD_RETPROBE) {
166 + if (mode != EBPF_LOAD_PROBE && mode != EBPF_LOAD_RETPROBE) {
167 ret = sync_bpf__attach(obj);
168 } else {
169 obj->links.netdata_sync_kprobe = bpf_program__attach_kprobe(obj->progs.netdata_sync_kprobe, false, target);
@@ -280,7 +192,7 @@ ebpf_sync_load_and_attach(struct sync_bpf *obj, ebpf_module_t *em, char *target,
192 void ebpf_sync_cleanup_objects()
193 {
194 int i;
283 - for (i = 0; local_syscalls[i].syscall; i++) {
195 + for (i = 0; i < NETDATA_SYNC_IDX_END; i++) {
196 ebpf_sync_syscalls_t *w = &local_syscalls[i];
197 #ifdef LIBBPF_MAJOR_VERSION
198 if (w->sync_obj) {
@@ -296,20 +208,6 @@ void ebpf_sync_cleanup_objects()
208 }
209 }
210
299 -/*
300 - static void ebpf_create_sync_chart(char *id,
301 - char *title,
302 - int order,
303 - int idx,
304 - int end,
305 - int update_every)
306 - {
307 - ebpf_write_chart_cmd(NETDATA_EBPF_MEMORY_GROUP, id, title, EBPF_COMMON_UNITS_CALL,
308 - NETDATA_EBPF_SYNC_SUBMENU, NETDATA_EBPF_CHART_TYPE_LINE, NULL, order,
309 - update_every,
310 - NETDATA_EBPF_MODULE_NAME_SYNC);
311 - */
312 -
211 /**
212 * Obsolete global
213 *
@@ -329,7 +227,7 @@ static void ebpf_obsolete_sync_global(ebpf_module_t *em)
227 NETDATA_EBPF_SYNC_SUBMENU,
228 NETDATA_EBPF_CHART_TYPE_LINE,
229 "mem.file_sync",
332 - 21300,
230 + NETDATA_EBPF_FILE_SYNC_CHART_ORDER,
231 em->update_every);
232
233 if (local_syscalls[NETDATA_SYNC_MSYNC_IDX].enabled)
@@ -342,7 +240,7 @@ static void ebpf_obsolete_sync_global(ebpf_module_t *em)
240 NETDATA_EBPF_SYNC_SUBMENU,
241 NETDATA_EBPF_CHART_TYPE_LINE,
242 "mem.memory_map",
345 - 21301,
243 + NETDATA_EBPF_MSYNC_CHART_ORDER,
244 em->update_every);
245
246 if (local_syscalls[NETDATA_SYNC_SYNC_IDX].enabled && local_syscalls[NETDATA_SYNC_SYNCFS_IDX].enabled)
@@ -355,7 +253,7 @@ static void ebpf_obsolete_sync_global(ebpf_module_t *em)
253 NETDATA_EBPF_SYNC_SUBMENU,
254 NETDATA_EBPF_CHART_TYPE_LINE,
255 "mem.sync",
358 - 21302,
256 + NETDATA_EBPF_SYNC_CHART_ORDER,
257 em->update_every);
258
259 if (local_syscalls[NETDATA_SYNC_SYNC_FILE_RANGE_IDX].enabled)
@@ -368,7 +266,7 @@ static void ebpf_obsolete_sync_global(ebpf_module_t *em)
266 NETDATA_EBPF_SYNC_SUBMENU,
267 NETDATA_EBPF_CHART_TYPE_LINE,
268 "mem.file_segment",
371 - 21303,
269 + NETDATA_EBPF_FILE_SEGMENT_CHART_ORDER,
270 em->update_every);
271 }
272
@@ -379,23 +277,28 @@ static void ebpf_obsolete_sync_global(ebpf_module_t *em)
277 *
278 * @param ptr thread data.
279 */
280 +void ebpf_sync_unload_bpf(ebpf_module_t *em __maybe_unused)
281 +{
282 + ebpf_sync_cleanup_objects();
283 +}
284 +
285 static void ebpf_sync_exit(void *pptr)
286 {
287 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
288 if (!em)
289 return;
290
388 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
291 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
292 netdata_mutex_lock(&lock);
293 ebpf_obsolete_sync_global(em);
294 netdata_mutex_unlock(&lock);
295 }
296
394 - ebpf_sync_cleanup_objects();
297 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
298 + em->functions.bpf_unload(em);
299
300 netdata_mutex_lock(&ebpf_exit_cleanup);
301 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
398 - ebpf_update_stats(&plugin_statistics, em);
302 netdata_mutex_unlock(&ebpf_exit_cleanup);
303 }
304
@@ -438,18 +341,18 @@ static int ebpf_sync_load_legacy(ebpf_sync_syscalls_t *w, ebpf_module_t *em)
341 static int ebpf_sync_initialize_syscall(ebpf_module_t *em)
342 {
343 #ifdef LIBBPF_MAJOR_VERSION
441 - ebpf_define_map_type(sync_maps, em->maps_per_core, running_on_kernel);
442 - ebpf_define_map_type(syncfs_maps, em->maps_per_core, running_on_kernel);
443 - ebpf_define_map_type(msync_maps, em->maps_per_core, running_on_kernel);
444 - ebpf_define_map_type(fsync_maps, em->maps_per_core, running_on_kernel);
445 - ebpf_define_map_type(fdatasync_maps, em->maps_per_core, running_on_kernel);
446 - ebpf_define_map_type(sync_file_range_maps, em->maps_per_core, running_on_kernel);
344 + ebpf_define_map_type(sync_maps[NETDATA_SYNC_SYNC_IDX], em->maps_per_core, running_on_kernel);
345 + ebpf_define_map_type(sync_maps[NETDATA_SYNC_SYNCFS_IDX], em->maps_per_core, running_on_kernel);
346 + ebpf_define_map_type(sync_maps[NETDATA_SYNC_MSYNC_IDX], em->maps_per_core, running_on_kernel);
347 + ebpf_define_map_type(sync_maps[NETDATA_SYNC_FSYNC_IDX], em->maps_per_core, running_on_kernel);
348 + ebpf_define_map_type(sync_maps[NETDATA_SYNC_FDATASYNC_IDX], em->maps_per_core, running_on_kernel);
349 + ebpf_define_map_type(sync_maps[NETDATA_SYNC_SYNC_FILE_RANGE_IDX], em->maps_per_core, running_on_kernel);
350 #endif
351
352 int i;
353 const char *saved_name = em->info.thread_name;
354 int errors = 0;
452 - for (i = 0; local_syscalls[i].syscall; i++) {
355 + for (i = 0; i < NETDATA_SYNC_IDX_END; i++) {
356 ebpf_sync_syscalls_t *w = &local_syscalls[i];
357 w->sync_maps = local_syscalls[i].sync_maps;
358 em->maps = local_syscalls[i].sync_maps;
@@ -471,6 +374,8 @@ static int ebpf_sync_initialize_syscall(ebpf_module_t *em)
374 errors++;
375 } else {
376 if (ebpf_sync_load_and_attach(w->sync_obj, em, syscall, i)) {
377 + sync_bpf__destroy(w->sync_obj);
378 + w->sync_obj = NULL;
379 w->enabled = false;
380 errors++;
381 }
@@ -512,7 +417,7 @@ static void ebpf_sync_read_global_table(int maps_per_core)
417 netdata_idx_t stored[NETDATA_MAX_PROCESSOR];
418 uint32_t idx = NETDATA_SYNC_CALL;
419 int i;
515 - for (i = 0; local_syscalls[i].syscall; i++) {
420 + for (i = 0; i < NETDATA_SYNC_IDX_END; i++) {
421 ebpf_sync_syscalls_t *w = &local_syscalls[i];
422 if (w->enabled) {
423 int fd = w->sync_maps[NETDATA_SYNC_GLOBAL_TABLE].map_fd;
@@ -597,8 +502,14 @@ static void sync_collector(ebpf_module_t *em)
502 heartbeat_t hb;
503 heartbeat_init(&hb, USEC_PER_SEC);
504 while (!ebpf_plugin_stop() && running_time < lifetime) {
505 + if (ebpf_plugin_stop())
506 + break;
507 +
508 heartbeat_next(&hb);
601 - if (ebpf_plugin_stop() || ++counter != update_every)
509 + if (ebpf_plugin_stop())
510 + break;
511 +
512 + if (++counter != update_every)
513 continue;
514
515 counter = 0;
@@ -609,12 +520,11 @@ static void sync_collector(ebpf_module_t *em)
520
521 netdata_mutex_unlock(&lock);
522
612 - netdata_mutex_lock(&ebpf_exit_cleanup);
613 - if (running_time && !em->running_time)
614 - running_time = update_every;
615 - else
616 - running_time += update_every;
523 + if (ebpf_plugin_stop())
524 + break;
525
526 + netdata_mutex_lock(&ebpf_exit_cleanup);
527 + running_time += update_every;
528 em->running_time = running_time;
529 netdata_mutex_unlock(&ebpf_exit_cleanup);
530 }
@@ -678,7 +588,7 @@ static void ebpf_create_sync_charts(int update_every)
588 ebpf_create_sync_chart(
589 NETDATA_EBPF_FILE_SYNC_CHART,
590 "Monitor calls to fsync(2) and fdatasync(2).",
681 - 21300,
591 + NETDATA_EBPF_FILE_SYNC_CHART_ORDER,
592 NETDATA_SYNC_FSYNC_IDX,
593 NETDATA_SYNC_FDATASYNC_IDX,
594 update_every,
@@ -688,7 +598,7 @@ static void ebpf_create_sync_charts(int update_every)
598 ebpf_create_sync_chart(
599 NETDATA_EBPF_MSYNC_CHART,
600 "Monitor calls to msync(2).",
691 - 21301,
601 + NETDATA_EBPF_MSYNC_CHART_ORDER,
602 NETDATA_SYNC_MSYNC_IDX,
603 NETDATA_SYNC_MSYNC_IDX,
604 update_every,
@@ -698,7 +608,7 @@ static void ebpf_create_sync_charts(int update_every)
608 ebpf_create_sync_chart(
609 NETDATA_EBPF_SYNC_CHART,
610 "Monitor calls to sync(2) and syncfs(2).",
701 - 21302,
611 + NETDATA_EBPF_SYNC_CHART_ORDER,
612 NETDATA_SYNC_SYNC_IDX,
613 NETDATA_SYNC_SYNCFS_IDX,
614 update_every,
@@ -708,7 +618,7 @@ static void ebpf_create_sync_charts(int update_every)
618 ebpf_create_sync_chart(
619 NETDATA_EBPF_FILE_SEGMENT_CHART,
620 "Monitor calls to sync_file_range(2).",
711 - 21303,
621 + NETDATA_EBPF_FILE_SEGMENT_CHART_ORDER,
622 NETDATA_SYNC_SYNC_FILE_RANGE_IDX,
623 NETDATA_SYNC_SYNC_FILE_RANGE_IDX,
624 update_every,
@@ -725,8 +635,8 @@ static void ebpf_create_sync_charts(int update_every)
635 static void ebpf_sync_parse_syscalls()
636 {
637 for (int i = 0; local_syscalls[i].syscall; i++) {
728 - local_syscalls[i].enabled = inicfg_get_boolean(&sync_config, NETDATA_SYNC_CONFIG_NAME,
729 - local_syscalls[i].syscall, CONFIG_BOOLEAN_YES);
638 + local_syscalls[i].enabled =
639 + inicfg_get_boolean(&sync_config, NETDATA_SYNC_CONFIG_NAME, local_syscalls[i].syscall, CONFIG_BOOLEAN_YES);
640 }
641 }
642
@@ -738,12 +648,13 @@ static void ebpf_sync_parse_syscalls()
648 */
649 static void ebpf_set_sync_maps()
650 {
741 - local_syscalls[NETDATA_SYNC_SYNC_IDX].sync_maps = sync_maps;
742 - local_syscalls[NETDATA_SYNC_SYNCFS_IDX].sync_maps = syncfs_maps;
743 - local_syscalls[NETDATA_SYNC_MSYNC_IDX].sync_maps = msync_maps;
744 - local_syscalls[NETDATA_SYNC_FSYNC_IDX].sync_maps = fsync_maps;
745 - local_syscalls[NETDATA_SYNC_FDATASYNC_IDX].sync_maps = fdatasync_maps;
746 - local_syscalls[NETDATA_SYNC_SYNC_FILE_RANGE_IDX].sync_maps = sync_file_range_maps;
651 + ebpf_initialize_sync_maps();
652 + local_syscalls[NETDATA_SYNC_SYNC_IDX].sync_maps = sync_maps[NETDATA_SYNC_SYNC_IDX];
653 + local_syscalls[NETDATA_SYNC_SYNCFS_IDX].sync_maps = sync_maps[NETDATA_SYNC_SYNCFS_IDX];
654 + local_syscalls[NETDATA_SYNC_MSYNC_IDX].sync_maps = sync_maps[NETDATA_SYNC_MSYNC_IDX];
655 + local_syscalls[NETDATA_SYNC_FSYNC_IDX].sync_maps = sync_maps[NETDATA_SYNC_FSYNC_IDX];
656 + local_syscalls[NETDATA_SYNC_FDATASYNC_IDX].sync_maps = sync_maps[NETDATA_SYNC_FDATASYNC_IDX];
657 + local_syscalls[NETDATA_SYNC_SYNC_FILE_RANGE_IDX].sync_maps = sync_maps[NETDATA_SYNC_SYNC_FILE_RANGE_IDX];
658 }
659
660 /**
@@ -761,6 +672,10 @@ void ebpf_sync_thread(void *ptr)
672
673 CLEANUP_FUNCTION_REGISTER(ebpf_sync_exit) cleanup_ptr = em;
674
675 + if (!ebpf_module_thread_has_valid_state(em)) {
676 + goto endsync;
677 + }
678 +
679 ebpf_set_sync_maps();
680 ebpf_sync_parse_syscalls();
681
src/collectors/ebpf.plugin/ebpf_sync.h
+7
@@ -3,6 +3,8 @@
3 #ifndef NETDATA_EBPF_SYNC_H
4 #define NETDATA_EBPF_SYNC_H 1
5
6 +#include "libbpf_api/ebpf.h"
7 +
8 // Module name & description
9 #define NETDATA_EBPF_MODULE_NAME_SYNC "sync"
10 #define NETDATA_EBPF_SYNC_MODULE_DESC \
@@ -24,6 +26,11 @@
26
27 #define NETDATA_EBPF_SYNC_SLEEP_MS 800000ULL
28
29 +#define NETDATA_EBPF_FILE_SYNC_CHART_ORDER 21300
30 +#define NETDATA_EBPF_MSYNC_CHART_ORDER 21301
31 +#define NETDATA_EBPF_SYNC_CHART_ORDER 21302
32 +#define NETDATA_EBPF_FILE_SEGMENT_CHART_ORDER 21303
33 +
34 // configuration file
35 #define NETDATA_SYNC_CONFIG_FILE "sync.conf"
36 #define NETDATA_SYNC_CONFIG_NAME "syscalls"
src/collectors/ebpf.plugin/ebpf_unittest.c
+699 -3
@@ -2,8 +2,36 @@
2
3 #include "ebpf_unittest.h"
4
5 +#include <stdio.h>
6 +#include <stdlib.h>
7 +#include <string.h>
8 +#include <stdint.h>
9 +#include <arpa/inet.h>
10 +#include <netinet/in.h>
11 +#include "libbpf_api/ebpf_library.h"
12 +#include "ebpf.h"
13 +#include "ebpf_socket.h"
14 +
15 +extern uint32_t integration_with_collectors;
16 +extern int running_on_kernel;
17 +extern int isrh;
18 +extern ebpf_module_t ebpf_modules[];
19 +extern char *ebpf_algorithms[];
20 +
21 ebpf_module_t test_em;
22
23 +static int tests_failed = 0;
24 +
25 +#define EBPF_UT_ASSERT(test, msg) \
26 + do { \
27 + if (!(test)) { \
28 + fprintf(stderr, ">>> FAILED: %s\n", msg); \
29 + tests_failed++; \
30 + } else { \
31 + fprintf(stderr, ">>> PASSED: %s\n", msg); \
32 + } \
33 + } while (0)
34 +
35 /**
36 * Initialize structure
37 *
@@ -70,14 +98,682 @@ int ebpf_ut_load_real_binary()
98 */
99 int ebpf_ut_load_fake_binary()
100 {
101 + char *fake_name = strdupz("I_am_not_here");
102 + if (!fake_name)
103 + return -1;
104 +
105 const char *original = test_em.info.thread_name;
106 + test_em.info.thread_name = fake_name;
107
75 - test_em.info.thread_name = strdupz("I_am_not_here");
108 int ret = ebpf_ut_load_binary();
109
78 - ebpf_ut_cleanup_memory();
79 -
110 + freez(fake_name);
111 test_em.info.thread_name = original;
112
113 return !ret;
114 }
115 +
116 +/**
117 + * Test write_chart_dimension
118 + *
119 + * Tests the write_chart_dimension function to ensure it correctly
120 + * formats dimension output for charting.
121 + */
122 +static void test_write_chart_dimension(void)
123 +{
124 + fprintf(stderr, "\n=== Testing write_chart_dimension ===\n");
125 +
126 + fprintf(stderr, "--- Expected output: SET dimension_name = 12345 ---\n");
127 + fprintf(stderr, "--- Actual output: ");
128 + write_chart_dimension("dimension_name", 12345);
129 + fprintf(stderr, "---\n");
130 +}
131 +
132 +/**
133 + * Test ebpf_write_global_dimension
134 + *
135 + * Tests the ebpf_write_global_dimension function to ensure it correctly
136 + * formats global dimension output for charting.
137 + */
138 +static void test_ebpf_write_global_dimension(void)
139 +{
140 + fprintf(stderr, "\n=== Testing ebpf_write_global_dimension ===\n");
141 +
142 + fprintf(stderr, "--- Expected output: DIMENSION name id algorithm ---\n");
143 + fprintf(stderr, "--- Actual output: ");
144 + ebpf_write_global_dimension("name", "id", "algorithm");
145 + fprintf(stderr, "---\n");
146 +}
147 +
148 +/**
149 + * Test ebpf_write_chart_cmd
150 + *
151 + * Tests the ebpf_write_chart_cmd function to ensure it correctly
152 + * formats chart command output.
153 + */
154 +static void test_ebpf_write_chart_cmd(void)
155 +{
156 + fprintf(stderr, "\n=== Testing ebpf_write_chart_cmd ===\n");
157 +
158 + fprintf(stderr, "--- Testing chart command output ---\n");
159 + ebpf_write_chart_cmd("type", "id", "_suffix", "title", "units", "family", "charttype", "context", 100, 1, "module");
160 +}
161 +
162 +/**
163 + * Test ebpf_write_chart_obsolete
164 + *
165 + * Tests the ebpf_write_chart_obsolete function to ensure it correctly
166 + * formats obsolete chart output.
167 + */
168 +static void test_ebpf_write_chart_obsolete(void)
169 +{
170 + fprintf(stderr, "\n=== Testing ebpf_write_chart_obsolete ===\n");
171 +
172 + fprintf(stderr, "--- Testing obsolete chart output ---\n");
173 + ebpf_write_chart_obsolete("type", "id", "_suffix", "title", "units", "family", "charttype", "context", 100, 1);
174 +}
175 +
176 +/**
177 + * Test ebpf_clean_ip_structure
178 + *
179 + * Tests the ebpf_clean_ip_structure function to ensure it correctly
180 + * frees allocated IP list structures and clears the list pointer.
181 + */
182 +static void test_ebpf_clean_ip_structure(void)
183 +{
184 + fprintf(stderr, "\n=== Testing ebpf_clean_ip_structure ===\n");
185 +
186 + ebpf_network_viewer_ip_list_t *list = NULL;
187 + ebpf_network_viewer_ip_list_t *item1, *item2;
188 +
189 + item1 = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
190 + item1->value = strdupz("192.168.1.1");
191 + item1->ver = AF_INET;
192 + item1->next = NULL;
193 +
194 + item2 = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
195 + item2->value = strdupz("10.0.0.1");
196 + item2->ver = AF_INET;
197 + item2->next = NULL;
198 +
199 + list = item1;
200 + item1->next = item2;
201 +
202 + EBPF_UT_ASSERT(list != NULL, "List should not be NULL before cleaning");
203 + EBPF_UT_ASSERT(list->next != NULL, "List should have two items before cleaning");
204 +
205 + ebpf_clean_ip_structure(&list);
206 +
207 + EBPF_UT_ASSERT(list == NULL, "List should be NULL after cleaning");
208 +}
209 +
210 +/**
211 + * Test ebpf_clean_port_structure
212 + *
213 + * Tests the ebpf_clean_port_structure function to ensure it correctly
214 + * frees allocated port list structures and clears the list pointer.
215 + */
216 +static void test_ebpf_clean_port_structure(void)
217 +{
218 + fprintf(stderr, "\n=== Testing ebpf_clean_port_structure ===\n");
219 +
220 + ebpf_network_viewer_port_list_t *list = NULL;
221 + ebpf_network_viewer_port_list_t *item1, *item2;
222 +
223 + item1 = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
224 + item1->value = strdupz("80");
225 + item1->first = htons(80);
226 + item1->last = htons(80);
227 + item1->next = NULL;
228 +
229 + item2 = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
230 + item2->value = strdupz("443");
231 + item2->first = htons(443);
232 + item2->last = htons(443);
233 + item2->next = NULL;
234 +
235 + list = item1;
236 + item1->next = item2;
237 +
238 + EBPF_UT_ASSERT(list != NULL, "Port list should not be NULL before cleaning");
239 + EBPF_UT_ASSERT(list->next != NULL, "Port list should have two items before cleaning");
240 +
241 + ebpf_clean_port_structure(&list);
242 +
243 + EBPF_UT_ASSERT(list == NULL, "Port list should be NULL after cleaning");
244 +}
245 +
246 +/**
247 + * Test ebpf_how_to_load
248 + *
249 + * Tests the ebpf_how_to_load function to ensure it correctly parses
250 + * load mode strings and sets the appropriate thread mode.
251 + */
252 +static void test_ebpf_how_to_load(void)
253 +{
254 + fprintf(stderr, "\n=== Testing ebpf_how_to_load ===\n");
255 +
256 + ebpf_set_thread_mode(MODE_ENTRY);
257 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_ENTRY, "Initial mode should be MODE_ENTRY");
258 +
259 + ebpf_how_to_load("return");
260 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_RETURN, "Mode should be MODE_RETURN after 'return'");
261 +
262 + ebpf_how_to_load("entry");
263 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_ENTRY, "Mode should be MODE_ENTRY after 'entry'");
264 +
265 + ebpf_how_to_load("default");
266 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_ENTRY, "Mode should be MODE_ENTRY after 'default'");
267 +
268 + ebpf_how_to_load("invalid_mode");
269 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_ENTRY, "Mode should remain MODE_ENTRY after invalid input");
270 +}
271 +
272 +/**
273 + * Test ebpf_set_apps_mode
274 + *
275 + * Tests the ebpf_set_apps_mode function to ensure it correctly sets
276 + * the apps integration mode for all modules.
277 + */
278 +static void test_ebpf_set_apps_mode(void)
279 +{
280 + fprintf(stderr, "\n=== Testing ebpf_set_apps_mode ===\n");
281 +
282 + ebpf_set_apps_mode(NETDATA_EBPF_APPS_FLAG_YES);
283 + EBPF_UT_ASSERT(
284 + ebpf_modules[0].apps_charts == NETDATA_EBPF_APPS_FLAG_YES,
285 + "Apps mode should be set to NETDATA_EBPF_APPS_FLAG_YES");
286 +
287 + ebpf_set_apps_mode(NETDATA_EBPF_APPS_FLAG_NO);
288 + EBPF_UT_ASSERT(
289 + ebpf_modules[0].apps_charts == NETDATA_EBPF_APPS_FLAG_NO,
290 + "Apps mode should be set to NETDATA_EBPF_APPS_FLAG_NO");
291 +}
292 +
293 +/**
294 + * Test ebpf_set_thread_mode
295 + *
296 + * Tests the ebpf_set_thread_mode function to ensure it correctly sets
297 + * the run mode for all eBPF modules.
298 + */
299 +static void test_ebpf_set_thread_mode(void)
300 +{
301 + fprintf(stderr, "\n=== Testing ebpf_set_thread_mode ===\n");
302 +
303 + ebpf_set_thread_mode(MODE_RETURN);
304 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_RETURN, "Thread mode should be MODE_RETURN");
305 +
306 + ebpf_set_thread_mode(MODE_ENTRY);
307 + EBPF_UT_ASSERT(ebpf_modules[0].mode == MODE_ENTRY, "Thread mode should be MODE_ENTRY");
308 +}
309 +
310 +/**
311 + * Test ebpf_set_ipc_value
312 + *
313 + * Tests the ebpf_set_ipc_value function to ensure it correctly parses
314 + * integration strings and sets the appropriate IPC integration mode.
315 + */
316 +static void test_ebpf_set_ipc_value(void)
317 +{
318 + fprintf(stderr, "\n=== Testing ebpf_set_ipc_value ===\n");
319 +
320 + ebpf_set_ipc_value("shm");
321 + EBPF_UT_ASSERT(
322 + integration_with_collectors == NETDATA_EBPF_INTEGRATION_SHM,
323 + "Integration should be NETDATA_EBPF_INTEGRATION_SHM");
324 +
325 + ebpf_set_ipc_value("socket");
326 + EBPF_UT_ASSERT(
327 + integration_with_collectors == NETDATA_EBPF_INTEGRATION_SOCKET,
328 + "Integration should be NETDATA_EBPF_INTEGRATION_SOCKET");
329 +
330 + ebpf_set_ipc_value("disabled");
331 + EBPF_UT_ASSERT(
332 + integration_with_collectors == NETDATA_EBPF_INTEGRATION_DISABLED,
333 + "Integration should be NETDATA_EBPF_INTEGRATION_DISABLED");
334 +
335 + ebpf_set_ipc_value("invalid");
336 + EBPF_UT_ASSERT(
337 + integration_with_collectors == NETDATA_EBPF_INTEGRATION_DISABLED,
338 + "Integration should be DISABLED for invalid input");
339 +}
340 +
341 +/**
342 + * Test disable_all_global_charts
343 + *
344 + * Tests the disable_all_global_charts function to ensure it correctly
345 + * disables all global charts across all modules.
346 + */
347 +static void test_disable_all_global_charts(void)
348 +{
349 + fprintf(stderr, "\n=== Testing disable_all_global_charts ===\n");
350 +
351 + ebpf_modules[0].enabled = NETDATA_THREAD_EBPF_RUNNING;
352 + ebpf_modules[0].global_charts = 1;
353 +
354 + disable_all_global_charts();
355 +
356 + EBPF_UT_ASSERT(
357 + ebpf_modules[0].enabled == NETDATA_THREAD_EBPF_NOT_RUNNING,
358 + "Module should be disabled after disable_all_global_charts");
359 + EBPF_UT_ASSERT(ebpf_modules[0].global_charts == 0, "Global charts should be disabled");
360 +}
361 +
362 +/**
363 + * Test ebpf_disable_cgroups
364 + *
365 + * Tests the ebpf_disable_cgroups function to ensure it correctly
366 + * disables cgroup charts across all modules.
367 + */
368 +static void test_ebpf_disable_cgroups(void)
369 +{
370 + fprintf(stderr, "\n=== Testing ebpf_disable_cgroups ===\n");
371 +
372 + ebpf_modules[0].cgroup_charts = 1;
373 +
374 + ebpf_disable_cgroups();
375 +
376 + EBPF_UT_ASSERT(ebpf_modules[0].cgroup_charts == 0, "Cgroup charts should be disabled");
377 +}
378 +
379 +/**
380 + * Test ebpf_one_dimension_write_charts
381 + *
382 + * Tests the ebpf_one_dimension_write_charts function to ensure it correctly
383 + * formats single dimension chart output.
384 + */
385 +static void test_ebpf_one_dimension_write_charts(void)
386 +{
387 + fprintf(stderr, "\n=== Testing ebpf_one_dimension_write_charts ===\n");
388 +
389 + fprintf(stderr, "--- Testing single dimension chart output ---\n");
390 + ebpf_one_dimension_write_charts("family", "chart", "dimension", 42);
391 +}
392 +
393 +/**
394 + * Test write_io_chart
395 + *
396 + * Tests the write_io_chart function to ensure it correctly formats
397 + * I/O chart output with read and write dimensions.
398 + */
399 +static void test_write_io_chart(void)
400 +{
401 + fprintf(stderr, "\n=== Testing write_io_chart ===\n");
402 +
403 + fprintf(stderr, "--- Testing IO chart output ---\n");
404 + write_io_chart("chart", "family", "write_dim", 100, "read_dim", 200);
405 +}
406 +
407 +/**
408 + * Test write_histogram_chart
409 + *
410 + * Tests the write_histogram_chart function to ensure it correctly formats
411 + * histogram chart output with multiple dimensions.
412 + */
413 +static void test_write_histogram_chart(void)
414 +{
415 + fprintf(stderr, "\n=== Testing write_histogram_chart ===\n");
416 +
417 + uint64_t hist[4] = {10, 20, 30, 40};
418 + char *dims[4] = {"bucket1", "bucket2", "bucket3", "bucket4"};
419 +
420 + fprintf(stderr, "--- Testing histogram chart output ---\n");
421 + write_histogram_chart("family", "histogram", hist, dims, 4);
422 +}
423 +
424 +/**
425 + * Test ebpf_global_labels
426 + *
427 + * Tests the ebpf_global_labels function to ensure it correctly sets up
428 + * syscall labels and creates proper linked lists.
429 + */
430 +static void test_ebpf_global_labels(void)
431 +{
432 + fprintf(stderr, "\n=== Testing ebpf_global_labels ===\n");
433 +
434 + netdata_syscall_stat_t is[3];
435 + netdata_publish_syscall_t pio[3];
436 + char *dim[3] = {"dim1", "dim2", "dim3"};
437 + char *name[3] = {"name1", "name2", "name3"};
438 + int algorithm[3] = {0, 0, 0};
439 +
440 + memset(is, 0, sizeof(is));
441 + memset(pio, 0, sizeof(pio));
442 +
443 + ebpf_global_labels(is, pio, dim, name, algorithm, 3);
444 +
445 + EBPF_UT_ASSERT(is[0].next == &is[1], "is[0].next should point to is[1]");
446 + EBPF_UT_ASSERT(is[1].next == &is[2], "is[1].next should point to is[2]");
447 + EBPF_UT_ASSERT(is[2].next == NULL, "is[2].next should be NULL");
448 +
449 + EBPF_UT_ASSERT(pio[0].dimension == dim[0], "pio[0].dimension should be dim[0]");
450 + EBPF_UT_ASSERT(pio[1].dimension == dim[1], "pio[1].dimension should be dim[1]");
451 + EBPF_UT_ASSERT(pio[2].dimension == dim[2], "pio[2].dimension should be dim[2]");
452 +}
453 +
454 +/**
455 + * Test ebpf_parse_ports basic
456 + *
457 + * Tests the ebpf_parse_ports function with basic port numbers
458 + * to ensure it correctly parses and creates port list entries.
459 + */
460 +static void test_ebpf_parse_ports_basic(void)
461 +{
462 + fprintf(stderr, "\n=== Testing ebpf_parse_ports (basic) ===\n");
463 +
464 + network_viewer_opt.included_port = NULL;
465 + network_viewer_opt.excluded_port = NULL;
466 +
467 + ebpf_parse_ports("80 443");
468 +
469 + EBPF_UT_ASSERT(network_viewer_opt.included_port != NULL, "Port list should not be NULL after parsing '80 443'");
470 +
471 + ebpf_clean_port_structure(&network_viewer_opt.included_port);
472 + network_viewer_opt.included_port = NULL;
473 +}
474 +
475 +/**
476 + * Test ebpf_parse_ports with range
477 + *
478 + * Tests the ebpf_parse_ports function with port ranges
479 + * to ensure it correctly parses and creates port range entries.
480 + */
481 +static void test_ebpf_parse_ports_with_range(void)
482 +{
483 + fprintf(stderr, "\n=== Testing ebpf_parse_ports with range ===\n");
484 +
485 + network_viewer_opt.included_port = NULL;
486 +
487 + ebpf_parse_ports("8000-9000");
488 +
489 + EBPF_UT_ASSERT(network_viewer_opt.included_port != NULL, "Port list should not be NULL after parsing range");
490 +
491 + if (network_viewer_opt.included_port) {
492 + uint16_t first = ntohs(network_viewer_opt.included_port->first);
493 + uint16_t last = ntohs(network_viewer_opt.included_port->last);
494 + EBPF_UT_ASSERT(first == 8000, "First port should be 8000");
495 + EBPF_UT_ASSERT(last == 9000, "Last port should be 9000");
496 + }
497 +
498 + ebpf_clean_port_structure(&network_viewer_opt.included_port);
499 + network_viewer_opt.included_port = NULL;
500 +}
501 +
502 +/**
503 + * Test ebpf_parse_ips_unsafe basic
504 + *
505 + * Tests the ebpf_parse_ips_unsafe function with basic IPv4 addresses
506 + * to ensure it correctly parses and creates IP list entries.
507 + */
508 +static void test_ebpf_parse_ips_basic(void)
509 +{
510 + fprintf(stderr, "\n=== Testing ebpf_parse_ips_unsafe (basic) ===\n");
511 +
512 + network_viewer_opt.included_ips = NULL;
513 +
514 + ebpf_parse_ips_unsafe("192.168.1.1");
515 +
516 + EBPF_UT_ASSERT(network_viewer_opt.included_ips != NULL, "IP list should not be NULL after parsing IP");
517 +
518 + if (network_viewer_opt.included_ips) {
519 + EBPF_UT_ASSERT(network_viewer_opt.included_ips->ver == AF_INET, "IP should be IPv4");
520 + }
521 +
522 + ebpf_clean_ip_structure(&network_viewer_opt.included_ips);
523 + network_viewer_opt.included_ips = NULL;
524 +}
525 +
526 +/**
527 + * Test ebpf_parse_ips_unsafe with CIDR
528 + *
529 + * Tests the ebpf_parse_ips_unsafe function with CIDR notation
530 + * to ensure it correctly parses and creates IP range entries.
531 + */
532 +static void test_ebpf_parse_ips_with_cidr(void)
533 +{
534 + fprintf(stderr, "\n=== Testing ebpf_parse_ips_unsafe with CIDR ===\n");
535 +
536 + network_viewer_opt.included_ips = NULL;
537 +
538 + ebpf_parse_ips_unsafe("192.168.0.0/24");
539 +
540 + EBPF_UT_ASSERT(network_viewer_opt.included_ips != NULL, "IP list should not be NULL after parsing CIDR");
541 +
542 + ebpf_clean_ip_structure(&network_viewer_opt.included_ips);
543 + network_viewer_opt.included_ips = NULL;
544 +}
545 +
546 +/**
547 + * Test ebpf_print_help
548 + *
549 + * Tests the ebpf_print_help function to ensure it correctly
550 + * outputs help information to stderr.
551 + */
552 +static void test_ebpf_print_help(void)
553 +{
554 + fprintf(stderr, "\n=== Testing ebpf_print_help ===\n");
555 +
556 + fprintf(stderr, "--- Help output start ---\n");
557 + ebpf_print_help();
558 + fprintf(stderr, "--- Help output end ---\n");
559 +}
560 +
561 +/**
562 + * Test write_count_chart
563 + *
564 + * Tests the write_count_chart function to ensure it correctly
565 + * formats count chart output with syscall data.
566 + */
567 +static void test_write_count_chart(void)
568 +{
569 + fprintf(stderr, "\n=== Testing write_count_chart ===\n");
570 +
571 + netdata_publish_syscall_t publish[2];
572 + memset(publish, 0, sizeof(publish));
573 +
574 + publish[0].name = strdupz("call1");
575 + publish[0].ncall = 100;
576 + publish[0].next = &publish[1];
577 +
578 + publish[1].name = strdupz("call2");
579 + publish[1].ncall = 200;
580 + publish[1].next = NULL;
581 +
582 + fprintf(stderr, "--- Count chart output ---\n");
583 + write_count_chart("chart", "family", publish, 2);
584 +
585 + freez(publish[0].name);
586 + freez(publish[1].name);
587 +}
588 +
589 +/**
590 + * Test write_err_chart
591 + *
592 + * Tests the write_err_chart function to ensure it correctly
593 + * formats error chart output with syscall error data.
594 + */
595 +static void test_write_err_chart(void)
596 +{
597 + fprintf(stderr, "\n=== Testing write_err_chart ===\n");
598 +
599 + netdata_publish_syscall_t publish[2];
600 + memset(publish, 0, sizeof(publish));
601 +
602 + publish[0].name = strdupz("err1");
603 + publish[0].nerr = 5;
604 + publish[0].next = &publish[1];
605 +
606 + publish[1].name = strdupz("err2");
607 + publish[1].nerr = 10;
608 + publish[1].next = NULL;
609 +
610 + fprintf(stderr, "--- Error chart output ---\n");
611 + write_err_chart("chart", "family", publish, 2);
612 +
613 + freez(publish[0].name);
614 + freez(publish[1].name);
615 +}
616 +
617 +/**
618 + * Test ebpf_create_global_dimension
619 + *
620 + * Tests the ebpf_create_global_dimension function to ensure it correctly
621 + * creates global dimension entries from a linked list.
622 + */
623 +static void test_ebpf_create_global_dimension(void)
624 +{
625 + fprintf(stderr, "\n=== Testing ebpf_create_global_dimension ===\n");
626 +
627 + netdata_publish_syscall_t publish[3];
628 + memset(publish, 0, sizeof(publish));
629 +
630 + publish[0].name = strdupz("dim1");
631 + publish[0].dimension = strdupz("dim1_id");
632 + publish[0].algorithm = "absolute";
633 + publish[0].next = &publish[1];
634 +
635 + publish[1].name = strdupz("dim2");
636 + publish[1].dimension = strdupz("dim2_id");
637 + publish[1].algorithm = "absolute";
638 + publish[1].next = &publish[2];
639 +
640 + publish[2].name = strdupz("dim3");
641 + publish[2].dimension = strdupz("dim3_id");
642 + publish[2].algorithm = "absolute";
643 + publish[2].next = NULL;
644 +
645 + fprintf(stderr, "--- Global dimension output ---\n");
646 + ebpf_create_global_dimension(publish, 3);
647 +
648 + freez(publish[0].name);
649 + freez(publish[0].dimension);
650 + freez(publish[1].name);
651 + freez(publish[1].dimension);
652 + freez(publish[2].name);
653 + freez(publish[2].dimension);
654 +}
655 +
656 +/**
657 + * Test ebpf_enable_specific_chart
658 + *
659 + * Tests the ebpf_enable_specific_chart function to ensure it correctly
660 + * enables specific charts with proper flags.
661 + */
662 +static void test_ebpf_enable_specific_chart(void)
663 +{
664 + fprintf(stderr, "\n=== Testing ebpf_enable_specific_chart ===\n");
665 +
666 + ebpf_module_t test_module;
667 + memset(&test_module, 0, sizeof(test_module));
668 +
669 + ebpf_enable_specific_chart(&test_module, 0);
670 +
671 + EBPF_UT_ASSERT(test_module.enabled == NETDATA_THREAD_EBPF_RUNNING, "Module should be enabled");
672 + EBPF_UT_ASSERT(test_module.global_charts == CONFIG_BOOLEAN_YES, "Global charts should be enabled");
673 + EBPF_UT_ASSERT(
674 + test_module.cgroup_charts == CONFIG_BOOLEAN_YES, "Cgroup charts should be enabled when disable_cgroup is 0");
675 +
676 + memset(&test_module, 0, sizeof(test_module));
677 + ebpf_enable_specific_chart(&test_module, 1);
678 +
679 + EBPF_UT_ASSERT(test_module.cgroup_charts == 0, "Cgroup charts should be disabled when disable_cgroup is 1");
680 +}
681 +
682 +/**
683 + * Test ebpf_enable_chart
684 + *
685 + * Tests the ebpf_enable_chart function to ensure it correctly
686 + * enables charts by index.
687 + */
688 +static void test_ebpf_enable_chart(void)
689 +{
690 + fprintf(stderr, "\n=== Testing ebpf_enable_chart ===\n");
691 +
692 + ebpf_modules[0].enabled = NETDATA_THREAD_EBPF_NOT_RUNNING;
693 +
694 + ebpf_enable_chart(0, 0);
695 +
696 + EBPF_UT_ASSERT(ebpf_modules[0].enabled == NETDATA_THREAD_EBPF_RUNNING, "Chart at index 0 should be enabled");
697 +}
698 +
699 +/**
700 + * Test parse_network_viewer_section with NULL
701 + *
702 + * Tests the parse_network_viewer_section function with empty config
703 + * to ensure it sets appropriate default values.
704 + */
705 +static void test_parse_network_viewer_section_null(void)
706 +{
707 + fprintf(stderr, "\n=== Testing parse_network_viewer_section (NULL) ===\n");
708 +
709 + struct config cfg;
710 + memset(&cfg, 0, sizeof(cfg));
711 +
712 + parse_network_viewer_section(&cfg);
713 +
714 + EBPF_UT_ASSERT(
715 + network_viewer_opt.hostname_resolution_enabled == CONFIG_BOOLEAN_NO,
716 + "Hostname resolution should be disabled by default");
717 +}
718 +
719 +/**
720 + * Test ebpf_load_collector_config
721 + *
722 + * Tests the ebpf_load_collector_config function with non-existent path
723 + * to ensure it handles errors correctly.
724 + */
725 +static void test_ebpf_load_collector_config(void)
726 +{
727 + fprintf(stderr, "\n=== Testing ebpf_load_collector_config ===\n");
728 +
729 + int disable_cgroups = 0;
730 + int result = ebpf_load_collector_config("/tmp", &disable_cgroups, 1);
731 +
732 + EBPF_UT_ASSERT(result == -1, "Should return -1 for non-existent config path");
733 +}
734 +
735 +void ebpf_library_run_unittests(void)
736 +{
737 + fprintf(stderr, "\n");
738 + fprintf(stderr, "===========================================\n");
739 + fprintf(stderr, " EBPF Library Unit Tests\n");
740 + fprintf(stderr, "===========================================\n");
741 +
742 + test_write_chart_dimension();
743 + test_ebpf_write_global_dimension();
744 + test_ebpf_write_chart_cmd();
745 + test_ebpf_write_chart_obsolete();
746 + test_ebpf_clean_ip_structure();
747 + test_ebpf_clean_port_structure();
748 + test_ebpf_how_to_load();
749 + test_ebpf_set_apps_mode();
750 + test_ebpf_set_thread_mode();
751 + test_ebpf_set_ipc_value();
752 + test_disable_all_global_charts();
753 + test_ebpf_disable_cgroups();
754 + test_ebpf_one_dimension_write_charts();
755 + test_write_io_chart();
756 + test_write_histogram_chart();
757 + test_ebpf_global_labels();
758 + test_ebpf_parse_ports_basic();
759 + test_ebpf_parse_ports_with_range();
760 + test_ebpf_parse_ips_basic();
761 + test_ebpf_parse_ips_with_cidr();
762 + test_ebpf_print_help();
763 + test_write_count_chart();
764 + test_write_err_chart();
765 + test_ebpf_create_global_dimension();
766 + test_ebpf_enable_specific_chart();
767 + test_ebpf_enable_chart();
768 + test_parse_network_viewer_section_null();
769 + test_ebpf_load_collector_config();
770 +
771 + fprintf(stderr, "\n");
772 + fprintf(stderr, "===========================================\n");
773 + if (tests_failed == 0) {
774 + fprintf(stderr, " All tests PASSED\n");
775 + } else {
776 + fprintf(stderr, " %d tests FAILED\n", tests_failed);
777 + }
778 + fprintf(stderr, "===========================================\n");
779 +}
src/collectors/ebpf.plugin/ebpf_unittest.h
+1
@@ -7,4 +7,5 @@ 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 +void ebpf_library_run_unittests(void);
11 #endif
src/collectors/ebpf.plugin/ebpf_vfs.c
+144 -97
@@ -2,6 +2,8 @@
2
3 #include "ebpf.h"
4 #include "ebpf_vfs.h"
5 +#include "libbpf_api/ebpf_library.h"
6 +#include <stddef.h>
7
8 static char *vfs_dimension_names[NETDATA_KEY_PUBLISH_VFS_END] = {"delete", "read", "write", "fsync", "open", "create"};
9 static char *vfs_id_names[NETDATA_KEY_PUBLISH_VFS_END] =
@@ -270,42 +272,6 @@ static int ebpf_vfs_attach_probe(struct vfs_bpf *obj)
272 if (ret)
273 return -1;
274
273 - obj->links.netdata_vfs_fsync_kprobe = bpf_program__attach_kprobe(
274 - obj->progs.netdata_vfs_fsync_kprobe, false, vfs_targets[NETDATA_EBPF_VFS_FSYNC].name);
275 - ret = libbpf_get_error(obj->links.netdata_vfs_fsync_kprobe);
276 - if (ret)
277 - return -1;
278 -
279 - obj->links.netdata_vfs_fsync_kretprobe = bpf_program__attach_kprobe(
280 - obj->progs.netdata_vfs_fsync_kretprobe, true, vfs_targets[NETDATA_EBPF_VFS_FSYNC].name);
281 - ret = libbpf_get_error(obj->links.netdata_vfs_fsync_kretprobe);
282 - if (ret)
283 - return -1;
284 -
285 - obj->links.netdata_vfs_open_kprobe =
286 - bpf_program__attach_kprobe(obj->progs.netdata_vfs_open_kprobe, false, vfs_targets[NETDATA_EBPF_VFS_OPEN].name);
287 - ret = libbpf_get_error(obj->links.netdata_vfs_open_kprobe);
288 - if (ret)
289 - return -1;
290 -
291 - obj->links.netdata_vfs_open_kretprobe = bpf_program__attach_kprobe(
292 - obj->progs.netdata_vfs_open_kretprobe, true, vfs_targets[NETDATA_EBPF_VFS_OPEN].name);
293 - ret = libbpf_get_error(obj->links.netdata_vfs_open_kretprobe);
294 - if (ret)
295 - return -1;
296 -
297 - obj->links.netdata_vfs_create_kprobe = bpf_program__attach_kprobe(
298 - obj->progs.netdata_vfs_create_kprobe, false, vfs_targets[NETDATA_EBPF_VFS_CREATE].name);
299 - ret = libbpf_get_error(obj->links.netdata_vfs_create_kprobe);
300 - if (ret)
301 - return -1;
302 -
303 - obj->links.netdata_vfs_create_kretprobe = bpf_program__attach_kprobe(
304 - obj->progs.netdata_vfs_create_kretprobe, true, vfs_targets[NETDATA_EBPF_VFS_CREATE].name);
305 - ret = libbpf_get_error(obj->links.netdata_vfs_create_kretprobe);
306 - if (ret)
307 - return -1;
308 -
275 return 0;
276 }
277
@@ -546,7 +512,7 @@ static void ebpf_obsolete_vfs_services(ebpf_module_t *em, char *id)
512 EBPF_COMMON_UNITS_CALLS_PER_SEC,
513 NETDATA_VFS_GROUP,
514 NETDATA_EBPF_CHART_TYPE_STACKED,
549 - NETDATA_SYSTEMD_VFS_OPEN_ERROR_CONTEXT,
515 + NETDATA_SYSTEMD_VFS_CREATE_CONTEXT,
516 20076,
517 em->update_every);
518
@@ -915,6 +881,21 @@ static void ebpf_obsolete_vfs_global(ebpf_module_t *em)
881 *
882 * @param ptr thread data.
883 **/
884 +void ebpf_vfs_unload_bpf(ebpf_module_t *em)
885 +{
886 +#ifdef LIBBPF_MAJOR_VERSION
887 + if (vfs_bpf_obj) {
888 + vfs_bpf__destroy(vfs_bpf_obj);
889 + vfs_bpf_obj = NULL;
890 + }
891 +#endif
892 + if ((em->load & EBPF_LOAD_LEGACY) && em->probe_links) {
893 + ebpf_unload_legacy_code(em->objects, em->probe_links);
894 + em->objects = NULL;
895 + em->probe_links = NULL;
896 + }
897 +}
898 +
899 static void ebpf_vfs_exit(void *pptr)
900 {
901 ebpf_module_t *em = CLEANUP_FUNCTION_GET_PTR(pptr);
@@ -928,7 +909,7 @@ static void ebpf_vfs_exit(void *pptr)
909 if (ebpf_read_vfs.thread)
910 nd_thread_signal_cancel(ebpf_read_vfs.thread);
911
931 - if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
912 + if (em->enabled == NETDATA_THREAD_EBPF_FUNCTION_RUNNING && !ebpf_plugin_stop()) {
913 netdata_mutex_lock(&lock);
914 if (em->cgroup_charts) {
915 ebpf_obsolete_vfs_cgroup_charts(em);
@@ -945,23 +926,11 @@ static void ebpf_vfs_exit(void *pptr)
926 netdata_mutex_unlock(&lock);
927 }
928
948 - ebpf_update_kernel_memory_with_vector(&plugin_statistics, em->maps, EBPF_ACTION_STAT_REMOVE);
949 -
950 -#ifdef LIBBPF_MAJOR_VERSION
951 - if (vfs_bpf_obj) {
952 - vfs_bpf__destroy(vfs_bpf_obj);
953 - vfs_bpf_obj = NULL;
954 - }
955 -#endif
956 - if (em->objects) {
957 - ebpf_unload_legacy_code(em->objects, em->probe_links);
958 - em->objects = NULL;
959 - em->probe_links = NULL;
960 - }
929 + if (!ebpf_plugin_stop() && em->functions.bpf_unload)
930 + em->functions.bpf_unload(em);
931
932 netdata_mutex_lock(&ebpf_exit_cleanup);
933 em->enabled = NETDATA_THREAD_EBPF_STOPPED;
964 - ebpf_update_stats(&plugin_statistics, em);
934 netdata_mutex_unlock(&ebpf_exit_cleanup);
935 }
936
@@ -1084,7 +1053,6 @@ static void ebpf_vfs_read_global_table(netdata_idx_t *stats, int maps_per_core)
1053 vfs_aggregated_data[NETDATA_KEY_PUBLISH_VFS_READ].bytes =
1054 (uint64_t)res[NETDATA_KEY_BYTES_VFS_READ] + (uint64_t)res[NETDATA_KEY_BYTES_VFS_READV];
1055 }
1087 -
1056 /**
1057 * Set VFS
1058 *
@@ -1095,6 +1063,7 @@ static void ebpf_vfs_read_global_table(netdata_idx_t *stats, int maps_per_core)
1063 */
1064 static inline void vfs_aggregate_set_vfs(netdata_publish_vfs_t *vfs, netdata_ebpf_vfs_t *w)
1065 {
1066 + vfs->ct = w->ct;
1067 vfs->write_call = w->write_call;
1068 vfs->writev_call = w->writev_call;
1069 vfs->read_call = w->read_call;
@@ -1103,12 +1072,10 @@ static inline void vfs_aggregate_set_vfs(netdata_publish_vfs_t *vfs, netdata_ebp
1072 vfs->fsync_call = w->fsync_call;
1073 vfs->open_call = w->open_call;
1074 vfs->create_call = w->create_call;
1106 -
1075 vfs->write_bytes = w->write_bytes;
1076 vfs->writev_bytes = w->writev_bytes;
1109 - vfs->read_bytes = w->read_bytes;
1077 vfs->readv_bytes = w->readv_bytes;
1111 -
1078 + vfs->read_bytes = w->read_bytes;
1079 vfs->write_err = w->write_err;
1080 vfs->writev_err = w->writev_err;
1081 vfs->read_err = w->read_err;
@@ -1129,6 +1096,12 @@ static inline void vfs_aggregate_set_vfs(netdata_publish_vfs_t *vfs, netdata_ebp
1096 */
1097 static inline void vfs_aggregate_publish_vfs(netdata_publish_vfs_t *vfs, netdata_publish_vfs_t *w)
1098 {
1099 + vfs->ct += w->ct;
1100 + vfs->write_bytes += w->write_bytes;
1101 + vfs->writev_bytes += w->writev_bytes;
1102 + vfs->readv_bytes += w->readv_bytes;
1103 + vfs->read_bytes += w->read_bytes;
1104 +
1105 vfs->write_call += w->write_call;
1106 vfs->writev_call += w->writev_call;
1107 vfs->read_call += w->read_call;
@@ -1138,11 +1111,6 @@ static inline void vfs_aggregate_publish_vfs(netdata_publish_vfs_t *vfs, netdata
1111 vfs->open_call += w->open_call;
1112 vfs->create_call += w->create_call;
1113
1141 - vfs->write_bytes += w->write_bytes;
1142 - vfs->writev_bytes += w->writev_bytes;
1143 - vfs->read_bytes += w->read_bytes;
1144 - vfs->readv_bytes += w->readv_bytes;
1145 -
1114 vfs->write_err += w->write_err;
1115 vfs->writev_err += w->writev_err;
1116 vfs->read_err += w->read_err;
@@ -1188,6 +1156,9 @@ void ebpf_vfs_send_apps_data(ebpf_module_t *em, struct ebpf_target *root)
1156 struct ebpf_target *w;
1157 netdata_mutex_lock(&collect_data_mutex);
1158 for (w = root; w; w = w->next) {
1159 + if (ebpf_plugin_stop())
1160 + break;
1161 +
1162 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_VFS_IDX))))
1163 continue;
1164
@@ -1268,25 +1239,35 @@ static void vfs_apps_accumulator(netdata_ebpf_vfs_t *out, int maps_per_core)
1239 int i, end = (maps_per_core) ? ebpf_nprocs : 1;
1240 netdata_ebpf_vfs_t *total = &out[0];
1241 uint64_t ct = total->ct;
1242 +
1243 for (i = 1; i < end; i++) {
1244 + if (ebpf_plugin_stop())
1245 + break;
1246 +
1247 netdata_ebpf_vfs_t *w = &out[i];
1248
1249 + total->write_bytes += w->write_bytes;
1250 + total->writev_bytes += w->writev_bytes;
1251 + total->readv_bytes += w->readv_bytes;
1252 + total->read_bytes += w->read_bytes;
1253 +
1254 total->write_call += w->write_call;
1255 total->writev_call += w->writev_call;
1256 total->read_call += w->read_call;
1257 total->readv_call += w->readv_call;
1258 total->unlink_call += w->unlink_call;
1279 -
1280 - total->write_bytes += w->write_bytes;
1281 - total->writev_bytes += w->writev_bytes;
1282 - total->read_bytes += w->read_bytes;
1283 - total->readv_bytes += w->readv_bytes;
1259 + total->fsync_call += w->fsync_call;
1260 + total->open_call += w->open_call;
1261 + total->create_call += w->create_call;
1262
1263 total->write_err += w->write_err;
1264 total->writev_err += w->writev_err;
1265 total->read_err += w->read_err;
1266 total->readv_err += w->readv_err;
1267 total->unlink_err += w->unlink_err;
1268 + total->fsync_err += w->fsync_err;
1269 + total->open_err += w->open_err;
1270 + total->create_err += w->create_err;
1271
1272 if (w->ct > ct)
1273 ct = w->ct;
@@ -1294,6 +1275,8 @@ static void vfs_apps_accumulator(netdata_ebpf_vfs_t *out, int maps_per_core)
1275 if (!total->name[0] && w->name[0])
1276 strncpyz(total->name, w->name, sizeof(total->name) - 1);
1277 }
1278 +
1279 + total->ct = ct;
1280 }
1281
1282 /**
@@ -1309,7 +1292,10 @@ static void ebpf_vfs_read_apps(int maps_per_core)
1292
1293 uint32_t key = 0, next_key = 0;
1294 while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
1312 - if (bpf_map_lookup_elem(fd, &key, vv)) {
1295 + if (ebpf_plugin_stop())
1296 + break;
1297 +
1298 + if (bpf_map_lookup_elem(fd, &key, vv) != 0) {
1299 goto end_vfs_loop;
1300 }
1301
@@ -1348,6 +1334,9 @@ static void read_update_vfs_cgroup()
1334 ebpf_cgroup_target_t *ect;
1335 netdata_mutex_lock(&mutex_cgroup_shm);
1336 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
1337 + if (ebpf_plugin_stop())
1338 + break;
1339 +
1340 struct pid_on_target2 *pids;
1341 for (pids = ect->pids; pids; pids = pids->next) {
1342 uint32_t pid = pids->pid;
@@ -1368,7 +1357,7 @@ static void read_update_vfs_cgroup()
1357 /**
1358 * Sum PIDs
1359 *
1371 - * Sum values for all targets.
1360 + * Sum values for all targets and maintain monotonicity.
1361 *
1362 * @param vfs structure used to store data
1363 * @param pids input data
@@ -1381,6 +1370,12 @@ static void ebpf_vfs_sum_cgroup_pids(netdata_publish_vfs_t *vfs, struct pid_on_t
1370 while (pids) {
1371 netdata_publish_vfs_t *w = &pids->vfs;
1372
1373 + accumulator.ct += w->ct;
1374 + accumulator.write_bytes += w->write_bytes;
1375 + accumulator.writev_bytes += w->writev_bytes;
1376 + accumulator.readv_bytes += w->readv_bytes;
1377 + accumulator.read_bytes += w->read_bytes;
1378 +
1379 accumulator.write_call += w->write_call;
1380 accumulator.writev_call += w->writev_call;
1381 accumulator.read_call += w->read_call;
@@ -1390,11 +1385,6 @@ static void ebpf_vfs_sum_cgroup_pids(netdata_publish_vfs_t *vfs, struct pid_on_t
1385 accumulator.open_call += w->open_call;
1386 accumulator.create_call += w->create_call;
1387
1393 - accumulator.write_bytes += w->write_bytes;
1394 - accumulator.writev_bytes += w->writev_bytes;
1395 - accumulator.read_bytes += w->read_bytes;
1396 - accumulator.readv_bytes += w->readv_bytes;
1397 -
1388 accumulator.write_err += w->write_err;
1389 accumulator.writev_err += w->writev_err;
1390 accumulator.read_err += w->read_err;
@@ -1407,7 +1397,12 @@ static void ebpf_vfs_sum_cgroup_pids(netdata_publish_vfs_t *vfs, struct pid_on_t
1397 pids = pids->next;
1398 }
1399
1410 - // These conditions were added, because we are using incremental algorithm
1400 + vfs->ct = (accumulator.ct >= vfs->ct) ? accumulator.ct : vfs->ct;
1401 + vfs->write_bytes = (accumulator.write_bytes >= vfs->write_bytes) ? accumulator.write_bytes : vfs->write_bytes;
1402 + vfs->writev_bytes = (accumulator.writev_bytes >= vfs->writev_bytes) ? accumulator.writev_bytes : vfs->writev_bytes;
1403 + vfs->readv_bytes = (accumulator.readv_bytes >= vfs->readv_bytes) ? accumulator.readv_bytes : vfs->readv_bytes;
1404 + vfs->read_bytes = (accumulator.read_bytes >= vfs->read_bytes) ? accumulator.read_bytes : vfs->read_bytes;
1405 +
1406 vfs->write_call = (accumulator.write_call >= vfs->write_call) ? accumulator.write_call : vfs->write_call;
1407 vfs->writev_call = (accumulator.writev_call >= vfs->writev_call) ? accumulator.writev_call : vfs->writev_call;
1408 vfs->read_call = (accumulator.read_call >= vfs->read_call) ? accumulator.read_call : vfs->read_call;
@@ -1417,11 +1412,6 @@ static void ebpf_vfs_sum_cgroup_pids(netdata_publish_vfs_t *vfs, struct pid_on_t
1412 vfs->open_call = (accumulator.open_call >= vfs->open_call) ? accumulator.open_call : vfs->open_call;
1413 vfs->create_call = (accumulator.create_call >= vfs->create_call) ? accumulator.create_call : vfs->create_call;
1414
1420 - vfs->write_bytes = (accumulator.write_bytes >= vfs->write_bytes) ? accumulator.write_bytes : vfs->write_bytes;
1421 - vfs->writev_bytes = (accumulator.writev_bytes >= vfs->writev_bytes) ? accumulator.writev_bytes : vfs->writev_bytes;
1422 - vfs->read_bytes = (accumulator.read_bytes >= vfs->read_bytes) ? accumulator.read_bytes : vfs->read_bytes;
1423 - vfs->readv_bytes = (accumulator.readv_bytes >= vfs->readv_bytes) ? accumulator.readv_bytes : vfs->readv_bytes;
1424 -
1415 vfs->write_err = (accumulator.write_err >= vfs->write_err) ? accumulator.write_err : vfs->write_err;
1416 vfs->writev_err = (accumulator.writev_err >= vfs->writev_err) ? accumulator.writev_err : vfs->writev_err;
1417 vfs->read_err = (accumulator.read_err >= vfs->read_err) ? accumulator.read_err : vfs->read_err;
@@ -2125,6 +2115,9 @@ static void ebpf_create_systemd_vfs_charts(ebpf_module_t *em)
2115
2116 ebpf_cgroup_target_t *w;
2117 for (w = ebpf_cgroup_pids; w; w = w->next) {
2118 + if (ebpf_plugin_stop())
2119 + break;
2120 +
2121 if (unlikely(!w->systemd || w->flags & NETDATA_EBPF_SERVICES_HAS_VFS_CHART))
2122 continue;
2123
@@ -2169,6 +2162,9 @@ static void ebpf_send_systemd_vfs_charts(ebpf_module_t *em)
2162 {
2163 ebpf_cgroup_target_t *ect;
2164 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
2165 + if (ebpf_plugin_stop())
2166 + break;
2167 +
2168 if (unlikely(!(ect->flags & NETDATA_EBPF_SERVICES_HAS_VFS_CHART))) {
2169 continue;
2170 }
@@ -2250,6 +2246,11 @@ static void ebpf_vfs_send_cgroup_data(ebpf_module_t *em)
2246 ebpf_vfs_sum_cgroup_pids(&ect->publish_systemd_vfs, ect->pids);
2247 }
2248
2249 + if (ebpf_plugin_stop()) {
2250 + netdata_mutex_unlock(&mutex_cgroup_shm);
2251 + return;
2252 + }
2253 +
2254 if (shm_ebpf_cgroup.header->systemd_enabled) {
2255 if (send_cgroup_chart) {
2256 ebpf_create_systemd_vfs_charts(em);
@@ -2258,6 +2259,9 @@ static void ebpf_vfs_send_cgroup_data(ebpf_module_t *em)
2259 }
2260
2261 for (ect = ebpf_cgroup_pids; ect; ect = ect->next) {
2262 + if (ebpf_plugin_stop())
2263 + break;
2264 +
2265 if (ect->systemd)
2266 continue;
2267
@@ -2287,6 +2291,9 @@ void ebpf_vfs_resume_apps_data()
2291 struct ebpf_target *w;
2292 netdata_mutex_lock(&collect_data_mutex);
2293 for (w = apps_groups_root_target; w; w = w->next) {
2294 + if (ebpf_plugin_stop())
2295 + break;
2296 +
2297 if (unlikely(!(w->charts_created & (1 << EBPF_MODULE_VFS_IDX))))
2298 continue;
2299
@@ -2319,30 +2326,48 @@ void ebpf_read_vfs_thread(void *ptr)
2326 uint32_t lifetime = em->lifetime;
2327 int cgroups = em->cgroup_charts;
2328 uint32_t running_time = 0;
2322 - pids_fd[NETDATA_EBPF_PIDS_VFS_IDX] = vfs_maps[NETDATA_VFS_PID].map_fd;
2329 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_VFS_IDX, vfs_maps[NETDATA_VFS_PID].map_fd);
2330 heartbeat_t hb;
2324 - heartbeat_init(&hb, update_every * USEC_PER_SEC);
2331 + heartbeat_init(&hb, USEC_PER_SEC);
2332 while (!ebpf_plugin_stop() && running_time < lifetime) {
2333 + if (ebpf_plugin_stop())
2334 + break;
2335 +
2336 heartbeat_next(&hb);
2327 - if (ebpf_plugin_stop() || ++counter != update_every)
2337 + if (ebpf_plugin_stop())
2338 + break;
2339 +
2340 + if (++counter != update_every)
2341 continue;
2342
2330 - sem_wait(shm_mutex_ebpf_integration);
2343 + if (!ebpf_shm_sem_wait_or_stop(shm_mutex_ebpf_integration)) {
2344 + if (errno != ECANCELED)
2345 + netdata_log_error("VFS: Failed to wait on semaphore.");
2346 + break;
2347 + }
2348 ebpf_vfs_read_apps(maps_per_core);
2349 ebpf_vfs_resume_apps_data();
2350 + if (ebpf_plugin_stop()) {
2351 + if (sem_post(shm_mutex_ebpf_integration))
2352 + netdata_log_error("VFS: Failed to post semaphore.");
2353 + break;
2354 + }
2355 +
2356 if (cgroups && shm_ebpf_cgroup.header)
2357 read_update_vfs_cgroup();
2358
2336 - sem_post(shm_mutex_ebpf_integration);
2359 + if (sem_post(shm_mutex_ebpf_integration)) {
2360 + netdata_log_error("VFS: Failed to post semaphore.");
2361 + break;
2362 + }
2363
2364 counter = 0;
2365
2340 - netdata_mutex_lock(&ebpf_exit_cleanup);
2341 - if (running_time && !em->running_time)
2342 - running_time = update_every;
2343 - else
2344 - running_time += update_every;
2366 + if (ebpf_plugin_stop())
2367 + break;
2368
2369 + netdata_mutex_lock(&ebpf_exit_cleanup);
2370 + running_time += update_every;
2371 em->running_time = running_time;
2372 netdata_mutex_unlock(&ebpf_exit_cleanup);
2373 }
@@ -2367,14 +2392,23 @@ static void vfs_collector(ebpf_module_t *em)
2392 heartbeat_t hb;
2393 heartbeat_init(&hb, USEC_PER_SEC);
2394 while (!ebpf_plugin_stop() && running_time < lifetime) {
2395 + if (ebpf_plugin_stop())
2396 + break;
2397 +
2398 heartbeat_next(&hb);
2371 - if (ebpf_plugin_stop() || ++counter != update_every)
2399 + if (ebpf_plugin_stop())
2400 + break;
2401 +
2402 + if (++counter != update_every)
2403 continue;
2404
2405 counter = 0;
2406 netdata_apps_integration_flags_t apps = em->apps_charts;
2407 ebpf_vfs_read_global_table(stats, maps_per_core);
2408
2409 + if (ebpf_plugin_stop())
2410 + break;
2411 +
2412 netdata_mutex_lock(&lock);
2413
2414 ebpf_vfs_send_data(em);
@@ -2383,17 +2417,21 @@ static void vfs_collector(ebpf_module_t *em)
2417 if (apps & NETDATA_EBPF_APPS_FLAG_CHART_CREATED)
2418 ebpf_vfs_send_apps_data(em, apps_groups_root_target);
2419
2420 + if (ebpf_plugin_stop()) {
2421 + netdata_mutex_unlock(&lock);
2422 + break;
2423 + }
2424 +
2425 if (cgroups && shm_ebpf_cgroup.header)
2426 ebpf_vfs_send_cgroup_data(em);
2427
2428 netdata_mutex_unlock(&lock);
2429
2391 - netdata_mutex_lock(&ebpf_exit_cleanup);
2392 - if (running_time && !em->running_time)
2393 - running_time = update_every;
2394 - else
2395 - running_time += update_every;
2430 + if (ebpf_plugin_stop())
2431 + break;
2432
2433 + netdata_mutex_lock(&ebpf_exit_cleanup);
2434 + running_time += update_every;
2435 em->running_time = running_time;
2436 netdata_mutex_unlock(&ebpf_exit_cleanup);
2437 }
@@ -2895,8 +2933,13 @@ static int ebpf_vfs_load_bpf(ebpf_module_t *em)
2933 vfs_bpf_obj = vfs_bpf__open();
2934 if (!vfs_bpf_obj)
2935 ret = -1;
2898 - else
2936 + else {
2937 ret = ebpf_vfs_load_and_attach(vfs_bpf_obj, em);
2938 + if (ret) {
2939 + vfs_bpf__destroy(vfs_bpf_obj);
2940 + vfs_bpf_obj = NULL;
2941 + }
2942 + }
2943 }
2944 #endif
2945
@@ -2914,11 +2957,15 @@ static int ebpf_vfs_load_bpf(ebpf_module_t *em)
2957 */
2958 void ebpf_vfs_thread(void *ptr)
2959 {
2917 - pids_fd[NETDATA_EBPF_PIDS_VFS_IDX] = -1;
2960 + ebpf_set_pid_map_fd(NETDATA_EBPF_PIDS_VFS_IDX, -1);
2961 ebpf_module_t *em = (ebpf_module_t *)ptr;
2962
2963 CLEANUP_FUNCTION_REGISTER(ebpf_vfs_exit) cleanup_ptr = em;
2964
2965 + if (!ebpf_module_thread_has_valid_state(em)) {
2966 + goto endvfs;
2967 + }
2968 +
2969 em->maps = vfs_maps;
2970
2971 ebpf_update_pid_table(&vfs_maps[NETDATA_VFS_PID], em);
src/collectors/ebpf.plugin/libbpf_api/ebpf.c
+179 -122
@@ -6,7 +6,7 @@
6 #include <dlfcn.h>
7 #include <sys/utsname.h>
8
9 -#include "ebpf.h"
9 +#include "../ebpf.h"
10 #include "libnetdata/libnetdata.h"
11
12 char *ebpf_user_config_dir = CONFIG_DIR;
@@ -81,12 +81,13 @@ int ebpf_get_kernel_version()
81 if (fd < 0)
82 return -1;
83
84 - ssize_t len = read(fd, ver, sizeof(ver));
84 + ssize_t len = read(fd, ver, sizeof(ver) - 1);
85 if (len < 0) {
86 close(fd);
87 return -1;
88 }
89
90 + ver[len] = '\0';
91 close(fd);
92
93 char *move = major;
@@ -113,11 +114,19 @@ int ebpf_get_kernel_version()
114 // This new rule is fixing kernel version according the formula:
115 // KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
116 // that was extracted from /usr/include/linux/version.h
117 + long major_val = str2l(major);
118 + long minor_val = str2l(minor);
119 + if (major_val < 0 || minor_val < 0)
120 + return -1;
121 +
122 int ipatch = (int)str2l(patch);
123 + if (ipatch < 0)
124 + return -1;
125 +
126 if (ipatch > 255)
127 ipatch = 255;
128
120 - return ((int)(str2l(major) * 65536) + (int)(str2l(minor) * 256) + ipatch);
129 + return ((int)(major_val * 65536) + (int)(minor_val * 256) + ipatch);
130 }
131
132 /**
@@ -142,7 +151,7 @@ int get_redhat_release()
151 char *end = strchr(buffer, '.');
152 char *start;
153 if (end) {
145 - *end = 0x0;
154 + *end = '\0';
155
156 if (end > buffer) {
157 start = end - 1;
@@ -150,9 +159,9 @@ int get_redhat_release()
159 major = strtol(start, NULL, 10);
160 start = ++end;
161
153 - end++;
154 - if (end) {
155 - end = 0x00;
162 + char *minor_end = strchr(start, ' ');
163 + if (minor_end) {
164 + *minor_end = '\0';
165 minor = strtol(start, NULL, 10);
166 } else {
167 minor = -1;
@@ -182,8 +191,8 @@ static int kernel_is_rejected()
191 if (read_txt_file("/proc/version_signature", version_string, sizeof(version_string))) {
192 if (read_txt_file("/proc/version", version_string, sizeof(version_string))) {
193 struct utsname uname_buf;
185 - if (!uname(&uname_buf)) {
186 - netdata_log_info("Cannot check kernel version");
194 + if (uname(&uname_buf)) {
195 + collector_info("Cannot check kernel version");
196 return 0;
197 }
198 version_string_len =
@@ -227,19 +236,22 @@ static int kernel_is_rejected()
236 char *reject_string = NULL;
237 size_t buf_len = 0;
238 ssize_t reject_string_len;
230 - while ((reject_string_len = getline(&reject_string, &buf_len, kernel_reject_list) - 1) > 0) {
231 - if (version_string_len >= reject_string_len) {
232 - if (!strncmp(version_string, reject_string, reject_string_len)) {
233 - netdata_log_info("A buggy kernel is detected");
234 - fclose(kernel_reject_list);
235 - freez(reject_string);
236 - return 1;
239 + while ((reject_string_len = getline(&reject_string, &buf_len, kernel_reject_list)) > 0) {
240 + if (reject_string_len > 1) {
241 + reject_string_len--;
242 + if (version_string_len >= reject_string_len) {
243 + if (!strncmp(version_string, reject_string, reject_string_len)) {
244 + collector_info("A buggy kernel is detected");
245 + fclose(kernel_reject_list);
246 + freez(reject_string);
247 + return 1;
248 + }
249 }
250 }
251 }
252
253 fclose(kernel_reject_list);
242 - free(reject_string);
254 + freez(reject_string);
255
256 return 0;
257 }
@@ -300,7 +312,9 @@ int ebpf_can_plugin_load_code(int kver, char *plugin_name)
312 if (!is_ebpf_plugin_running_as_root()) {
313 netdata_log_error(
314 "%s should either run as root (now running with uid %u, euid %u) or have special capabilities.",
303 - plugin_name, (unsigned int)getuid(), (unsigned int)geteuid());
315 + plugin_name,
316 + (unsigned int)getuid(),
317 + (unsigned int)geteuid());
318 return -1;
319 }
320
@@ -316,7 +330,7 @@ int ebpf_can_plugin_load_code(int kver, char *plugin_name)
330 */
331 int ebpf_adjust_memory_limit()
332 {
319 - struct rlimit r = { RLIM_INFINITY, RLIM_INFINITY };
333 + struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
334 if (setrlimit(RLIMIT_MEMLOCK, &r)) {
335 netdata_log_error("Setrlimit(RLIMIT_MEMLOCK)");
336 return -1;
@@ -341,11 +355,18 @@ int ebpf_adjust_memory_limit()
355 */
356 static char *ebpf_select_kernel_name(uint32_t selector)
357 {
344 - static char *kernel_names[] = { NETDATA_IDX_STR_V3_10, NETDATA_IDX_STR_V4_14, NETDATA_IDX_STR_V4_16,
345 - NETDATA_IDX_STR_V4_18, NETDATA_IDX_STR_V5_4, NETDATA_IDX_STR_V5_10,
346 - NETDATA_IDX_STR_V5_11, NETDATA_IDX_STR_V5_14, NETDATA_IDX_STR_V5_15,
347 - NETDATA_IDX_STR_V5_16, NETDATA_IDX_STR_V6_8
348 - };
358 + static char *kernel_names[] = {
359 + NETDATA_IDX_STR_V3_10,
360 + NETDATA_IDX_STR_V4_14,
361 + NETDATA_IDX_STR_V4_16,
362 + NETDATA_IDX_STR_V4_18,
363 + NETDATA_IDX_STR_V5_4,
364 + NETDATA_IDX_STR_V5_10,
365 + NETDATA_IDX_STR_V5_11,
366 + NETDATA_IDX_STR_V5_14,
367 + NETDATA_IDX_STR_V5_15,
368 + NETDATA_IDX_STR_V5_16,
369 + NETDATA_IDX_STR_V6_8};
370
371 return kernel_names[selector];
372 }
@@ -372,7 +393,7 @@ static int ebpf_select_max_index(int is_rhf, uint32_t kver)
393 } else { // Kernels from kernel.org
394 if (kver >= NETDATA_EBPF_KERNEL_6_8)
395 return NETDATA_IDX_V6_8;
375 - else if (kver >= NETDATA_EBPF_KERNEL_5_16)
396 + else if (kver >= NETDATA_EBPF_KERNEL_5_16)
397 return NETDATA_IDX_V5_16;
398 else if (kver >= NETDATA_EBPF_KERNEL_5_15)
399 return NETDATA_IDX_V5_15;
@@ -438,16 +459,19 @@ static uint32_t ebpf_select_index(uint32_t kernels, int is_rhf, uint32_t kver)
459 * @param name the eBPF program name.
460 * @param is_return is return or entry ?
461 */
441 -static void ebpf_mount_name(char *out, size_t len, char *path, uint32_t kver, const char *name,
442 - int is_return, int is_rhf)
462 +static void
463 +ebpf_mount_name(char *out, size_t len, char *path, uint32_t kver, const char *name, int is_return, int is_rhf)
464 {
465 char *version = ebpf_select_kernel_name(kver);
445 - snprintfz(out, len, "%s/ebpf.d/%cnetdata_ebpf_%s.%s%s.o",
446 - path,
447 - (is_return) ? 'r' : 'p',
448 - name,
449 - version,
450 - (is_rhf != -1) ? ".rhf" : "");
466 + snprintfz(
467 + out,
468 + len,
469 + "%s/ebpf.d/%cnetdata_ebpf_%s.%s%s.o",
470 + path,
471 + (is_return) ? 'r' : 'p',
472 + name,
473 + version,
474 + (is_rhf != -1) ? ".rhf" : "");
475 }
476
477 //----------------------------------------------------------------------------------------------------------------------
@@ -543,31 +567,36 @@ void ebpf_update_stats(ebpf_plugin_stats_t *report, ebpf_module_t *em)
567 */
568 void ebpf_update_kernel_memory(ebpf_plugin_stats_t *report, ebpf_local_maps_t *map, ebpf_stats_action_t action)
569 {
546 - char filename[FILENAME_MAX+1];
570 + char filename[FILENAME_MAX + 1];
571 snprintfz(filename, FILENAME_MAX, "/proc/self/fdinfo/%d", map->map_fd);
572 procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
549 - if(unlikely(!ff)) {
573 + if (unlikely(!ff)) {
574 netdata_log_error("Cannot open %s", filename);
575 return;
576 }
577
578 ff = procfile_readall(ff);
555 - if(unlikely(!ff))
579 + if (unlikely(!ff))
580 return;
581
582 unsigned long j, lines = procfile_lines(ff);
559 - char *memlock = { "memlock" };
560 - for (j = 0; j < lines ; j++) {
561 - char *cmp = procfile_lineword(ff, j,0);
583 + char *memlock = "memlock";
584 + for (j = 0; j < lines; j++) {
585 + char *cmp = procfile_lineword(ff, j, 0);
586 if (!strncmp(memlock, cmp, 7)) {
563 - uint64_t memsize = (uint64_t) str2l(procfile_lineword(ff, j,1));
587 + uint64_t memsize = (uint64_t)str2l(procfile_lineword(ff, j, 1));
588 switch (action) {
589 case EBPF_ACTION_STAT_ADD: {
590 report->memlock_kern += memsize;
591 report->hash_tables += 1;
592 #ifdef NETDATA_DEV_MODE
569 - netdata_log_info("Hash table %u: %s (FD = %d) is consuming %lu bytes totalizing %lu bytes",
570 - report->hash_tables, map->name, map->map_fd, memsize, report->memlock_kern);
593 + collector_info(
594 + "Hash table %u: %s (FD = %d) is consuming %lu bytes totalizing %lu bytes",
595 + report->hash_tables,
596 + map->name,
597 + map->map_fd,
598 + memsize,
599 + report->memlock_kern);
600 #endif
601 break;
602 }
@@ -575,8 +604,13 @@ void ebpf_update_kernel_memory(ebpf_plugin_stats_t *report, ebpf_local_maps_t *m
604 report->memlock_kern -= memsize;
605 report->hash_tables -= 1;
606 #ifdef NETDATA_DEV_MODE
578 - netdata_log_info("Hash table %s (FD = %d) was removed releasing %lu bytes, now we have %u tables loaded totalizing %lu bytes.",
579 - map->name, map->map_fd, memsize, report->hash_tables, report->memlock_kern);
607 + collector_info(
608 + "Hash table %s (FD = %d) was removed releasing %lu bytes, now we have %u tables loaded totalizing %lu bytes.",
609 + map->name,
610 + map->map_fd,
611 + memsize,
612 + report->hash_tables,
613 + report->memlock_kern);
614 #endif
615 break;
616 }
@@ -601,9 +635,10 @@ void ebpf_update_kernel_memory(ebpf_plugin_stats_t *report, ebpf_local_maps_t *m
635 * @param map pointer to a map. Last map must fish with name = NULL
636 * @param action should plugin add or remove values from amount.
637 */
604 -void ebpf_update_kernel_memory_with_vector(ebpf_plugin_stats_t *report,
605 - ebpf_local_maps_t *maps,
606 - ebpf_stats_action_t action)
638 +void ebpf_update_kernel_memory_with_vector(
639 + ebpf_plugin_stats_t *report,
640 + ebpf_local_maps_t *maps,
641 + ebpf_stats_action_t action)
642 {
643 if (!maps)
644 return;
@@ -636,14 +671,18 @@ void ebpf_update_pid_table(ebpf_local_maps_t *pid, ebpf_module_t *em)
671 * @param em the structure with information about how the module/thread is working.
672 * @param map_name the name of the file used to log.
673 */
639 -void ebpf_update_map_size(struct bpf_map *map, ebpf_local_maps_t *lmap, ebpf_module_t *em, const char *map_name __maybe_unused)
674 +void ebpf_update_map_size(
675 + struct bpf_map *map,
676 + ebpf_local_maps_t *lmap,
677 + ebpf_module_t *em,
678 + const char *map_name __maybe_unused)
679 {
680 uint32_t define_size = 0;
681 uint32_t apps_type = NETDATA_EBPF_MAP_PID | NETDATA_EBPF_MAP_RESIZABLE;
682 if (lmap->user_input && lmap->user_input != lmap->internal_input) {
683 define_size = lmap->internal_input;
684 #ifdef NETDATA_INTERNAL_CHECKS
646 - netdata_log_info("Changing map %s from size %u to %u ", map_name, lmap->internal_input, lmap->user_input);
685 + collector_info("Changing map %s from size %u to %u ", map_name, lmap->internal_input, lmap->user_input);
686 #endif
687 } else if (((lmap->type & apps_type) == apps_type) && (!em->apps_charts) && (!em->cgroup_charts)) {
688 lmap->user_input = ND_EBPF_DEFAULT_MIN_PID;
@@ -794,7 +833,7 @@ static ebpf_specify_name_t *ebpf_find_names(ebpf_specify_name_t *names, const ch
833
834 static struct bpf_link **ebpf_attach_programs(struct bpf_object *obj, size_t length, ebpf_specify_name_t *names)
835 {
797 - struct bpf_link **links = callocz(length , sizeof(struct bpf_link *));
836 + struct bpf_link **links = callocz(length, sizeof(struct bpf_link *));
837 size_t i = 0;
838 struct bpf_program *prog;
839 ebpf_specify_name_t *w;
@@ -858,9 +897,7 @@ static void ebpf_update_maps(ebpf_module_t *em, struct bpf_object *obj)
897 void ebpf_update_controller(int fd, ebpf_module_t *em)
898 {
899 uint32_t values[NETDATA_CONTROLLER_END] = {
861 - (em->apps_charts & NETDATA_EBPF_APPS_FLAG_YES) | em->cgroup_charts,
862 - em->apps_level, 0, 0, 0, 0
863 - };
900 + (em->apps_charts & NETDATA_EBPF_APPS_FLAG_YES) | em->cgroup_charts, em->apps_level, 0, 0, 0, 0};
901 uint32_t key;
902 uint32_t end = NETDATA_CONTROLLER_PID_TABLE_ADD;
903
@@ -915,8 +952,7 @@ static void ebpf_update_legacy_controller(ebpf_module_t *em, struct bpf_object *
952 *
953 * @return it returns a link for each target we associated an eBPF program.
954 */
918 -struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kver, int is_rhf,
919 - struct bpf_object **obj)
955 +struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kver, int is_rhf, struct bpf_object **obj)
956 {
957 char lpath[4096];
958
@@ -925,15 +961,18 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv
961 ebpf_mount_name(lpath, 4095, plugins_dir, idx, em->info.thread_name, em->mode, is_rhf);
962
963 // When this function is called ebpf.plugin is using legacy code, so we should reset the variable
928 - em->load &= ~ NETDATA_EBPF_LOAD_METHODS;
964 + em->load &= ~NETDATA_EBPF_LOAD_METHODS;
965 em->load |= EBPF_LOAD_LEGACY;
966
967 *obj = bpf_object__open_file(lpath, NULL);
932 - if (!*obj)
968 + if (!*obj) {
969 + *obj = NULL;
970 return NULL;
971 + }
972
935 - if (libbpf_get_error(obj)) {
973 + if (libbpf_get_error(*obj)) {
974 bpf_object__close(*obj);
975 + *obj = NULL;
976 return NULL;
977 }
978
@@ -942,16 +981,17 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kv
981 if (bpf_object__load(*obj)) {
982 netdata_log_error("ERROR: loading BPF object file failed %s\n", lpath);
983 bpf_object__close(*obj);
984 + *obj = NULL;
985 return NULL;
986 }
987
988 ebpf_update_maps(em, *obj);
989 ebpf_update_legacy_controller(em, *obj);
990
951 - size_t count_programs = ebpf_count_programs(*obj);
991 + size_t count_programs = ebpf_count_programs(*obj);
992
993 #ifdef NETDATA_INTERNAL_CHECKS
954 - netdata_log_info("eBPF program %s loaded with success!", lpath);
994 + collector_info("eBPF program %s loaded with success!", lpath);
995 #endif
996
997 return ebpf_attach_programs(*obj, count_programs, em->names);
@@ -963,19 +1003,19 @@ char *ebpf_find_symbol(char *search)
1003 char *ret = NULL;
1004 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, NETDATA_KALLSYMS);
1005 procfile *ff = procfile_open(filename, " \t", PROCFILE_FLAG_DEFAULT);
966 - if(unlikely(!ff)) {
1006 + if (unlikely(!ff)) {
1007 netdata_log_error("Cannot open %s%s", netdata_configured_host_prefix, NETDATA_KALLSYMS);
1008 return ret;
1009 }
1010
1011 ff = procfile_readall(ff);
972 - if(unlikely(!ff))
1012 + if (unlikely(!ff))
1013 return ret;
1014
1015 unsigned long i, lines = procfile_lines(ff);
1016 size_t length = strlen(search);
977 - for(i = 0; i < lines ; i++) {
978 - char *cmp = procfile_lineword(ff, i,2);
1017 + for (i = 0; i < lines; i++) {
1018 + char *cmp = procfile_lineword(ff, i, 2);
1019 if (!strncmp(search, cmp, length)) {
1020 ret = strdupz(cmp);
1021 break;
@@ -1013,18 +1053,17 @@ int ebpf_load_config(struct config *config, char *filename)
1053 return inicfg_load(config, filename, 0, NULL);
1054 }
1055
1016 -
1056 static netdata_run_mode_t ebpf_select_mode(const char *mode)
1057 {
1019 - if (!strcasecmp(mode,EBPF_CFG_LOAD_MODE_RETURN ))
1058 + if (!strcasecmp(mode, EBPF_CFG_LOAD_MODE_RETURN))
1059 return MODE_RETURN;
1021 - else if (!strcasecmp(mode, "dev"))
1060 + else if (!strcasecmp(mode, "dev"))
1061 return MODE_DEVMODE;
1062
1063 return MODE_ENTRY;
1064 }
1065
1027 -static void ebpf_select_mode_string(char *output, size_t len, netdata_run_mode_t sel)
1066 +static void ebpf_select_mode_string(char *output, size_t len, netdata_run_mode_t sel)
1067 {
1068 if (sel == MODE_RETURN)
1069 strncpyz(output, EBPF_CFG_LOAD_MODE_RETURN, len);
@@ -1180,8 +1219,8 @@ struct btf *ebpf_load_btf_file(const char *path, const char *filename)
1219 snprintfz(fullpath, PATH_MAX, "%s/%s", path, filename);
1220 struct btf *ret = ebpf_parse_btf_file(fullpath);
1221 if (!ret)
1183 - netdata_log_info("Your environment does not have BTF file %s/%s. The plugin will work with 'legacy' code.",
1184 - path, filename);
1222 + collector_info(
1223 + "Your environment does not have BTF file %s/%s. The plugin will work with 'legacy' code.", path, filename);
1224
1225 return ret;
1226 }
@@ -1266,16 +1305,18 @@ static void ebpf_update_target_with_conf(ebpf_module_t *em, netdata_ebpf_program
1305 *
1306 * @return it returns the new load mode.
1307 */
1269 -static netdata_ebpf_load_mode_t ebpf_select_load_mode(struct btf *btf_file __maybe_unused,
1270 - netdata_ebpf_load_mode_t load,
1271 - int kver __maybe_unused,
1272 - int is_rh __maybe_unused)
1308 +static netdata_ebpf_load_mode_t ebpf_select_load_mode(
1309 + struct btf *btf_file __maybe_unused,
1310 + netdata_ebpf_load_mode_t load,
1311 + int kver __maybe_unused,
1312 + int is_rh __maybe_unused)
1313 {
1314 #ifdef LIBBPF_MAJOR_VERSION
1315 if ((load & EBPF_LOAD_CORE) || (load & EBPF_LOAD_PLAY_DICE)) {
1316 // Quick fix for Oracle linux 8.x
1317 load = (!btf_file || (is_rh && (kver >= NETDATA_EBPF_KERNEL_5_4 && kver < NETDATA_EBPF_KERNEL_5_5))) ?
1278 - EBPF_LOAD_LEGACY : EBPF_LOAD_CORE;
1318 + EBPF_LOAD_LEGACY :
1319 + EBPF_LOAD_CORE;
1320 }
1321 #else
1322 load = EBPF_LOAD_LEGACY;
@@ -1294,28 +1335,35 @@ static netdata_ebpf_load_mode_t ebpf_select_load_mode(struct btf *btf_file __may
1335 * @param btf_file a pointer to the loaded btf file.
1336 * @param is_rhf is Red Hat family?
1337 */
1297 -void ebpf_update_module_using_config(ebpf_module_t *modules, netdata_ebpf_load_mode_t origin, struct btf *btf_file,
1298 - int kver, int is_rh)
1338 +void ebpf_update_module_using_config(
1339 + ebpf_module_t *modules,
1340 + netdata_ebpf_load_mode_t origin,
1341 + struct btf *btf_file,
1342 + int kver,
1343 + int is_rh)
1344 {
1345 char default_value[EBPF_MAX_MODE_LENGTH + 1];
1346 ebpf_select_mode_string(default_value, EBPF_MAX_MODE_LENGTH, modules->mode);
1347 const char *load_mode = inicfg_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_LOAD_MODE, default_value);
1348 modules->mode = ebpf_select_mode(load_mode);
1349
1305 - modules->update_every = (int)inicfg_get_number(modules->cfg, EBPF_GLOBAL_SECTION,
1306 - EBPF_CFG_UPDATE_EVERY, modules->update_every);
1350 + modules->update_every =
1351 + (int)inicfg_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_UPDATE_EVERY, modules->update_every);
1352
1308 - modules->apps_charts = inicfg_get_boolean(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_APPLICATION,
1309 - (int) (modules->apps_charts & NETDATA_EBPF_APPS_FLAG_YES));
1353 + modules->apps_charts = inicfg_get_boolean(
1354 + modules->cfg,
1355 + EBPF_GLOBAL_SECTION,
1356 + EBPF_CFG_APPLICATION,
1357 + (int)(modules->apps_charts & NETDATA_EBPF_APPS_FLAG_YES));
1358
1311 - modules->cgroup_charts = inicfg_get_boolean(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_CGROUP,
1312 - modules->cgroup_charts);
1359 + modules->cgroup_charts =
1360 + inicfg_get_boolean(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_CGROUP, modules->cgroup_charts);
1361
1314 - modules->pid_map_size = (uint32_t)inicfg_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE,
1315 - modules->pid_map_size);
1362 + modules->pid_map_size =
1363 + (uint32_t)inicfg_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, modules->pid_map_size);
1364
1317 - modules->lifetime = (uint32_t) inicfg_get_number(modules->cfg, EBPF_GLOBAL_SECTION,
1318 - EBPF_CFG_LIFETIME, EBPF_DEFAULT_LIFETIME);
1365 + modules->lifetime =
1366 + (uint32_t)inicfg_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_LIFETIME, EBPF_DEFAULT_LIFETIME);
1367
1368 char *value = ebpf_convert_load_mode_to_string(modules->load & NETDATA_EBPF_LOAD_METHODS);
1369 const char *type_format = inicfg_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_TYPE_FORMAT, value);
@@ -1323,32 +1371,33 @@ void ebpf_update_module_using_config(ebpf_module_t *modules, netdata_ebpf_load_m
1371 load = ebpf_select_load_mode(btf_file, load, kver, is_rh);
1372 modules->load = origin | load;
1373
1326 - const char *core_attach = inicfg_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_CORE_ATTACH, EBPF_CFG_ATTACH_TRAMPOLINE);
1374 + const char *core_attach =
1375 + inicfg_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_CORE_ATTACH, EBPF_CFG_ATTACH_TRAMPOLINE);
1376 netdata_ebpf_program_loaded_t fill_lm = ebpf_convert_core_type(core_attach, modules->mode);
1377 ebpf_update_target_with_conf(modules, fill_lm);
1378
1379 value = ebpf_convert_collect_pid_to_string(modules->apps_level);
1380 const char *collect_pid = inicfg_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_COLLECT_PID, value);
1332 - modules->apps_level = ebpf_convert_string_to_apps_level(collect_pid);
1381 + modules->apps_level = ebpf_convert_string_to_apps_level(collect_pid);
1382
1334 - modules->maps_per_core = inicfg_get_boolean(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_MAPS_PER_CORE,
1335 - modules->maps_per_core);
1383 + modules->maps_per_core =
1384 + inicfg_get_boolean(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_MAPS_PER_CORE, modules->maps_per_core);
1385 if (kver < NETDATA_EBPF_KERNEL_4_06)
1386 modules->maps_per_core = CONFIG_BOOLEAN_NO;
1387
1388 #ifdef NETDATA_DEV_MODE
1340 - netdata_log_info("The thread %s was configured with: mode = %s; update every = %d; apps = %s; cgroup = %s; ebpf type format = %s; ebpf co-re tracing = %s; collect pid = %s; maps per core = %s, lifetime=%u",
1341 - modules->info.thread_name,
1342 - load_mode,
1343 - modules->update_every,
1344 - (modules->apps_charts)?"enabled":"disabled",
1345 - (modules->cgroup_charts)?"enabled":"disabled",
1346 - type_format,
1347 - core_attach,
1348 - collect_pid,
1349 - (modules->maps_per_core)?"enabled":"disabled",
1350 - modules->lifetime
1351 - );
1389 + collector_info(
1390 + "The thread %s was configured with: mode = %s; update every = %d; apps = %s; cgroup = %s; ebpf type format = %s; ebpf co-re tracing = %s; collect pid = %s; maps per core = %s, lifetime=%u",
1391 + modules->info.thread_name,
1392 + load_mode,
1393 + modules->update_every,
1394 + (modules->apps_charts) ? "enabled" : "disabled",
1395 + (modules->cgroup_charts) ? "enabled" : "disabled",
1396 + type_format,
1397 + core_attach,
1398 + collect_pid,
1399 + (modules->maps_per_core) ? "enabled" : "disabled",
1400 + modules->lifetime);
1401 #endif
1402 }
1403
@@ -1367,7 +1416,7 @@ void ebpf_update_module_using_config(ebpf_module_t *modules, netdata_ebpf_load_m
1416 */
1417 void ebpf_update_module(ebpf_module_t *em, struct btf *btf_file, int kver, int is_rh)
1418 {
1370 - char filename[FILENAME_MAX+1];
1419 + char filename[FILENAME_MAX + 1];
1420 netdata_ebpf_load_mode_t origin;
1421
1422 ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_user_config_dir, em->config_file);
@@ -1379,7 +1428,8 @@ void ebpf_update_module(ebpf_module_t *em, struct btf *btf_file, int kver, int i
1428 }
1429 // If user defined data globally, we will have here EBPF_LOADED_FROM_USER, we need to consider this, to avoid
1430 // forcing users to configure thread by thread.
1382 - origin = (!(em->load & NETDATA_EBPF_LOAD_SOURCE)) ? EBPF_LOADED_FROM_STOCK : em->load & NETDATA_EBPF_LOAD_SOURCE;
1431 + origin =
1432 + (!(em->load & NETDATA_EBPF_LOAD_SOURCE)) ? EBPF_LOADED_FROM_STOCK : em->load & NETDATA_EBPF_LOAD_SOURCE;
1433 } else
1434 origin = EBPF_LOADED_FROM_USER;
1435
@@ -1397,9 +1447,7 @@ void ebpf_update_module(ebpf_module_t *em, struct btf *btf_file, int kver, int i
1447 */
1448 void ebpf_adjust_apps_cgroup(ebpf_module_t *em, netdata_ebpf_program_loaded_t mode)
1449 {
1400 - if ((em->load & EBPF_LOADED_FROM_STOCK) &&
1401 - (em->apps_charts || em->cgroup_charts) &&
1402 - mode != EBPF_LOAD_TRAMPOLINE) {
1450 + if ((em->load & EBPF_LOADED_FROM_STOCK) && (em->apps_charts || em->cgroup_charts) && mode != EBPF_LOAD_TRAMPOLINE) {
1451 em->apps_charts = NETDATA_EBPF_APPS_FLAG_NO;
1452 em->cgroup_charts = 0;
1453 }
@@ -1419,7 +1467,7 @@ void ebpf_adjust_apps_cgroup(ebpf_module_t *em, netdata_ebpf_program_loaded_t mo
1467 void ebpf_load_addresses(ebpf_addresses_t *fa, int fd)
1468 {
1469 if (fa->addr)
1422 - return ;
1470 + return;
1471
1472 procfile *ff = procfile_open("/proc/kallsyms", " \t:", PROCFILE_FLAG_DEFAULT);
1473 if (!ff)
@@ -1432,16 +1480,19 @@ void ebpf_load_addresses(ebpf_addresses_t *fa, int fd)
1480 fa->hash = simple_hash(fa->function);
1481
1482 size_t lines = procfile_lines(ff), l;
1435 - for(l = 0; l < lines ;l++) {
1483 + for (l = 0; l < lines; l++) {
1484 char *fcnt = procfile_lineword(ff, l, 2);
1485 uint32_t hash = simple_hash(fcnt);
1486 if (fa->hash == hash && !strcmp(fcnt, fa->function)) {
1439 - char *type = procfile_lineword(ff, l, 2);
1487 + char *type = procfile_lineword(ff, l, 1);
1488 fa->type = type[0];
1489 + // Only text symbols (T=global, t=static, W=weak global, w=weak local) are probeable
1490 + if (fa->type != 'T' && fa->type != 't' && fa->type != 'W' && fa->type != 'w')
1491 + continue;
1492 if (fd > 0) {
1493 char addr[128];
1494 snprintf(addr, 127, "0x%s", procfile_lineword(ff, l, 0));
1444 - fa->addr = (unsigned long) strtoul(addr, NULL, 16);
1495 + fa->addr = (unsigned long)strtoul(addr, NULL, 16);
1496 uint32_t key = 0;
1497 bpf_map_update_elem(fd, &key, &fa->addr, BPF_ANY);
1498 } else
@@ -1479,7 +1530,7 @@ void ebpf_fill_algorithms(int *algorithms, size_t length, int algorithm)
1530 */
1531 char **ebpf_fill_histogram_dimension(size_t maximum)
1532 {
1482 - char *dimensions[] = { "us", "ms", "s"};
1533 + char *dimensions[] = {"us", "ms", "s"};
1534 int previous_dim = 0, current_dim = 0;
1535 uint32_t previous_level = 1000, current_level = 1000;
1536 uint32_t previous_divisor = 1, current_divisor = 1;
@@ -1489,8 +1540,14 @@ char **ebpf_fill_histogram_dimension(size_t maximum)
1540 char range[128];
1541 size_t end = maximum - 1;
1542 for (selector = 0; selector < end; selector++) {
1492 - snprintf(range, 127, "%u%s->%u%s", previous/previous_divisor, dimensions[previous_dim],
1493 - current/current_divisor, dimensions[current_dim]);
1543 + snprintf(
1544 + range,
1545 + 127,
1546 + "%u%s->%u%s",
1547 + previous / previous_divisor,
1548 + dimensions[previous_dim],
1549 + current / current_divisor,
1550 + dimensions[current_dim]);
1551 out[selector] = strdupz(range);
1552 previous = current;
1553 current <<= 1;
@@ -1509,7 +1566,7 @@ char **ebpf_fill_histogram_dimension(size_t maximum)
1566 current_level *= 1000;
1567 }
1568 }
1512 - snprintf(range, 127, "%u%s->+Inf", previous/previous_divisor, dimensions[previous_dim]);
1569 + snprintf(range, 127, "%u%s->+Inf", previous / previous_divisor, dimensions[previous_dim]);
1570 out[selector] = strdupz(range);
1571
1572 return out;
@@ -1545,7 +1602,8 @@ void ebpf_histogram_dimension_cleanup(char **ptr, size_t length)
1602 *
1603 * @return it returns a positive value on success and a negative otherwise.
1604 */
1548 -static inline int ebpf_open_tracepoint_path(char *filename, size_t length, char *subsys, char *eventname, int flags)
1605 +static inline int
1606 +ebpf_open_tracepoint_path(char *filename, size_t length, const char *subsys, const char *eventname, int flags)
1607 {
1608 snprintfz(filename, length, "%s/events/%s/%s/enable", NETDATA_DEBUGFS, subsys, eventname);
1609 return open(filename, flags | O_CLOEXEC, 0);
@@ -1561,7 +1619,7 @@ static inline int ebpf_open_tracepoint_path(char *filename, size_t length, char
1619 *
1620 * @return it returns 1 when it is enabled, 0 when it is disabled and -1 on error.
1621 */
1564 -int ebpf_is_tracepoint_enabled(char *subsys, char *eventname)
1622 +int ebpf_is_tracepoint_enabled(const char *subsys, const char *eventname)
1623 {
1624 char text[FILENAME_MAX + 1];
1625 int fd = ebpf_open_tracepoint_path(text, FILENAME_MAX, subsys, eventname, O_RDONLY);
@@ -1590,7 +1648,7 @@ int ebpf_is_tracepoint_enabled(char *subsys, char *eventname)
1648 *
1649 * @return It returns 0 on success and -1 otherwise
1650 */
1593 -static int ebpf_change_tracing_values(char *subsys, char *eventname, char *value)
1651 +static int ebpf_change_tracing_values(const char *subsys, const char *eventname, const char *value)
1652 {
1653 if (strcmp("0", value) && strcmp("1", value)) {
1654 netdata_log_error("Invalid value given to either enable or disable a tracepoint.");
@@ -1623,7 +1681,7 @@ static int ebpf_change_tracing_values(char *subsys, char *eventname, char *value
1681 *
1682 * @return It returns 0 on success and -1 otherwise
1683 */
1626 -int ebpf_enable_tracing_values(char *subsys, char *eventname)
1684 +int ebpf_enable_tracing_values(const char *subsys, const char *eventname)
1685 {
1686 return ebpf_change_tracing_values(subsys, eventname, "1");
1687 }
@@ -1638,7 +1696,7 @@ int ebpf_enable_tracing_values(char *subsys, char *eventname)
1696 *
1697 * @return It returns 0 on success and -1 otherwise
1698 */
1641 -int ebpf_disable_tracing_values(char *subsys, char *eventname)
1699 +int ebpf_disable_tracing_values(const char *subsys, const char *eventname)
1700 {
1701 return ebpf_change_tracing_values(subsys, eventname, "0");
1702 }
@@ -1680,4 +1738,3 @@ void ebpf_select_host_prefix(char *output, size_t length, char *syscall, int kve
1738 snprintfz(output, length, "%s_sys_%s", prefix, syscall);
1739 }
1740 }
1683 -
src/collectors/ebpf.plugin/libbpf_api/ebpf.h
+67 -63
@@ -82,23 +82,23 @@
82 *
83 */
84 enum netdata_ebpf_kernel_versions {
85 - NETDATA_EBPF_KERNEL_4_06 = 263680, // 264960 = 4 * 65536 + 6 * 256
86 - NETDATA_EBPF_KERNEL_4_11 = 264960, // 264960 = 4 * 65536 + 15 * 256
87 - NETDATA_EBPF_KERNEL_4_14 = 265728, // 264960 = 4 * 65536 + 14 * 256
88 - NETDATA_EBPF_KERNEL_4_15 = 265984, // 265984 = 4 * 65536 + 15 * 256
89 - NETDATA_EBPF_KERNEL_4_17 = 266496, // 266496 = 4 * 65536 + 17 * 256
90 - NETDATA_EBPF_KERNEL_5_0 = 327680, // 327680 = 5 * 65536 + 0 * 256
91 - NETDATA_EBPF_KERNEL_5_3 = 328448, // 327680 = 5 * 65536 + 3 * 256
92 - NETDATA_EBPF_KERNEL_5_4 = 328704, // 327680 = 5 * 65536 + 4 * 256
93 - NETDATA_EBPF_KERNEL_5_5 = 328960, // 327680 = 5 * 65536 + 5 * 256
94 - NETDATA_EBPF_KERNEL_5_9_16 = 330000, // 330240 = 5 * 65536 + 9 * 256 + 16
95 - NETDATA_EBPF_KERNEL_5_10 = 330240, // 330240 = 5 * 65536 + 10 * 256
96 - NETDATA_EBPF_KERNEL_5_11 = 330496, // 330240 = 5 * 65536 + 11 * 256
97 - NETDATA_EBPF_KERNEL_5_14 = 331264, // 331264 = 5 * 65536 + 14 * 256
98 - NETDATA_EBPF_KERNEL_5_15 = 331520, // 331520 = 5 * 65536 + 15 * 256
99 - NETDATA_EBPF_KERNEL_5_16 = 331776, // 331776 = 5 * 65536 + 16 * 256
100 - NETDATA_EBPF_KERNEL_6_8 = 395264, // 395264 = 6 * 65536 + 8 * 256
101 - NETDATA_EBPF_KERNEL_6_16 = 397312 // 397312 = 6 * 65536 + 16 * 256
85 + NETDATA_EBPF_KERNEL_4_06 = 263680, // 264960 = 4 * 65536 + 6 * 256
86 + NETDATA_EBPF_KERNEL_4_11 = 264960, // 264960 = 4 * 65536 + 15 * 256
87 + NETDATA_EBPF_KERNEL_4_14 = 265728, // 264960 = 4 * 65536 + 14 * 256
88 + NETDATA_EBPF_KERNEL_4_15 = 265984, // 265984 = 4 * 65536 + 15 * 256
89 + NETDATA_EBPF_KERNEL_4_17 = 266496, // 266496 = 4 * 65536 + 17 * 256
90 + NETDATA_EBPF_KERNEL_5_0 = 327680, // 327680 = 5 * 65536 + 0 * 256
91 + NETDATA_EBPF_KERNEL_5_3 = 328448, // 327680 = 5 * 65536 + 3 * 256
92 + NETDATA_EBPF_KERNEL_5_4 = 328704, // 327680 = 5 * 65536 + 4 * 256
93 + NETDATA_EBPF_KERNEL_5_5 = 328960, // 327680 = 5 * 65536 + 5 * 256
94 + NETDATA_EBPF_KERNEL_5_9_16 = 330000, // 330240 = 5 * 65536 + 9 * 256 + 16
95 + NETDATA_EBPF_KERNEL_5_10 = 330240, // 330240 = 5 * 65536 + 10 * 256
96 + NETDATA_EBPF_KERNEL_5_11 = 330496, // 330240 = 5 * 65536 + 11 * 256
97 + NETDATA_EBPF_KERNEL_5_14 = 331264, // 331264 = 5 * 65536 + 14 * 256
98 + NETDATA_EBPF_KERNEL_5_15 = 331520, // 331520 = 5 * 65536 + 15 * 256
99 + NETDATA_EBPF_KERNEL_5_16 = 331776, // 331776 = 5 * 65536 + 16 * 256
100 + NETDATA_EBPF_KERNEL_6_8 = 395264, // 395264 = 6 * 65536 + 8 * 256
101 + NETDATA_EBPF_KERNEL_6_16 = 397312 // 397312 = 6 * 65536 + 16 * 256
102 };
103
104 enum netdata_kernel_flag {
@@ -106,13 +106,13 @@ enum netdata_kernel_flag {
106 NETDATA_V4_14 = 1 << 1,
107 NETDATA_V4_16 = 1 << 2,
108 NETDATA_V4_18 = 1 << 3,
109 - NETDATA_V5_4 = 1 << 4,
109 + NETDATA_V5_4 = 1 << 4,
110 NETDATA_V5_10 = 1 << 5,
111 NETDATA_V5_11 = 1 << 6,
112 NETDATA_V5_14 = 1 << 7,
113 NETDATA_V5_15 = 1 << 8,
114 NETDATA_V5_16 = 1 << 9,
115 - NETDATA_V6_8 = 1 << 10
115 + NETDATA_V6_8 = 1 << 10
116 };
117
118 enum netdata_kernel_idx {
@@ -120,7 +120,7 @@ enum netdata_kernel_idx {
120 NETDATA_IDX_V4_14,
121 NETDATA_IDX_V4_16,
122 NETDATA_IDX_V4_18,
123 - NETDATA_IDX_V5_4 ,
123 + NETDATA_IDX_V5_4,
124 NETDATA_IDX_V5_10,
125 NETDATA_IDX_V5_11,
126 NETDATA_IDX_V5_14,
@@ -133,13 +133,13 @@ enum netdata_kernel_idx {
133 #define NETDATA_IDX_STR_V4_14 "4.14"
134 #define NETDATA_IDX_STR_V4_16 "4.16"
135 #define NETDATA_IDX_STR_V4_18 "4.18"
136 -#define NETDATA_IDX_STR_V5_4 "5.4"
136 +#define NETDATA_IDX_STR_V5_4 "5.4"
137 #define NETDATA_IDX_STR_V5_10 "5.10"
138 #define NETDATA_IDX_STR_V5_11 "5.11"
139 #define NETDATA_IDX_STR_V5_14 "5.14"
140 #define NETDATA_IDX_STR_V5_15 "5.15"
141 #define NETDATA_IDX_STR_V5_16 "5.16"
142 -#define NETDATA_IDX_STR_V6_8 "6.8"
142 +#define NETDATA_IDX_STR_V6_8 "6.8"
143
144 /**
145 * Minimum value has relationship with libbpf support.
@@ -216,7 +216,7 @@ typedef enum netdata_apps_level {
216 } netdata_apps_level_t;
217
218 typedef struct ebpf_local_maps {
219 - char *name;
219 + const char *name;
220 uint32_t internal_input;
221 uint32_t user_input;
222 uint32_t type;
@@ -234,20 +234,20 @@ typedef struct ebpf_specify_name {
234 } ebpf_specify_name_t;
235
236 typedef enum netdata_ebpf_load_mode {
237 - EBPF_LOAD_LEGACY = 1<<0, // Select legacy mode, this means we will load binaries
238 - EBPF_LOAD_CORE = 1<<1, // When CO-RE is used, it is necessary to use the source code
239 - EBPF_LOAD_PLAY_DICE = 1<<2, // Take a look on environment and choose the best option
240 - EBPF_LOADED_FROM_STOCK = 1<<3, // Configuration loaded from Stock file
241 - EBPF_LOADED_FROM_USER = 1<<4 // Configuration loaded from user
237 + EBPF_LOAD_LEGACY = 1 << 0, // Select legacy mode, this means we will load binaries
238 + EBPF_LOAD_CORE = 1 << 1, // When CO-RE is used, it is necessary to use the source code
239 + EBPF_LOAD_PLAY_DICE = 1 << 2, // Take a look on environment and choose the best option
240 + EBPF_LOADED_FROM_STOCK = 1 << 3, // Configuration loaded from Stock file
241 + EBPF_LOADED_FROM_USER = 1 << 4 // Configuration loaded from user
242 } netdata_ebpf_load_mode_t;
243 -#define NETDATA_EBPF_LOAD_METHODS (EBPF_LOAD_LEGACY|EBPF_LOAD_CORE|EBPF_LOAD_PLAY_DICE)
244 -#define NETDATA_EBPF_LOAD_SOURCE (EBPF_LOADED_FROM_STOCK|EBPF_LOADED_FROM_USER)
243 +#define NETDATA_EBPF_LOAD_METHODS (EBPF_LOAD_LEGACY | EBPF_LOAD_CORE | EBPF_LOAD_PLAY_DICE)
244 +#define NETDATA_EBPF_LOAD_SOURCE (EBPF_LOADED_FROM_STOCK | EBPF_LOADED_FROM_USER)
245
246 typedef enum netdata_ebpf_program_loaded {
247 - EBPF_LOAD_PROBE, // Attach probes on targets
248 - EBPF_LOAD_RETPROBE, // Attach retprobes on targets
249 - EBPF_LOAD_TRACEPOINT, // This stores log given description about the errors raised
250 - EBPF_LOAD_TRAMPOLINE, // This attaches kprobe when the function is called
247 + EBPF_LOAD_PROBE, // Attach probes on targets
248 + EBPF_LOAD_RETPROBE, // Attach retprobes on targets
249 + EBPF_LOAD_TRACEPOINT, // This stores log given description about the errors raised
250 + EBPF_LOAD_TRAMPOLINE, // This attaches kprobe when the function is called
251 } netdata_ebpf_program_loaded_t;
252
253 typedef struct netdata_ebpf_targets {
@@ -257,11 +257,11 @@ typedef struct netdata_ebpf_targets {
257
258 typedef struct ebpf_plugin_stats {
259 // Load options
260 - uint32_t legacy; // Legacy codes
261 - uint32_t core; // CO-RE codes, this means we are using source code compiled.
260 + uint32_t legacy; // Legacy codes
261 + uint32_t core; // CO-RE codes, this means we are using source code compiled.
262
263 - uint32_t threads; // Total number of threads
264 - uint32_t running; // total number of threads running
263 + uint32_t threads; // Total number of threads
264 + uint32_t running; // total number of threads running
265
266 uint32_t probes; // Number of kprobes loaded
267 uint32_t retprobes; // Number of kretprobes loaded
@@ -270,7 +270,7 @@ typedef struct ebpf_plugin_stats {
270
271 uint64_t memlock_kern; // The same information reported by bpftool, but it is not accurated
272 // https://lore.kernel.org/linux-mm/20230112155326.26902-5-laoar.shao@gmail.com/T/
273 - uint32_t hash_tables; // Number of hash tables used on the system.
273 + uint32_t hash_tables; // Number of hash tables used on the system.
274
275 uint32_t hash_percpu; // Number of threads running per cpu maps
276 uint32_t hash_unique; // Number of threads running an unique map for all cores.
@@ -292,18 +292,18 @@ typedef enum netdata_apps_integration_flags {
292 #define NETDATA_EBPF_STAT_DIMENSION_ARAL "aral"
293
294 enum ebpf_threads_status {
295 - NETDATA_THREAD_EBPF_RUNNING, // started by plugin
296 - NETDATA_THREAD_EBPF_FUNCTION_RUNNING, // started by function
297 - NETDATA_THREAD_EBPF_STOPPING, // stopping thread
298 - NETDATA_THREAD_EBPF_STOPPED, // thread stopped
299 - NETDATA_THREAD_EBPF_NOT_RUNNING // thread was never started
295 + NETDATA_THREAD_EBPF_RUNNING, // started by plugin
296 + NETDATA_THREAD_EBPF_FUNCTION_RUNNING, // started by function
297 + NETDATA_THREAD_EBPF_STOPPING, // stopping thread
298 + NETDATA_THREAD_EBPF_STOPPED, // thread stopped
299 + NETDATA_THREAD_EBPF_NOT_RUNNING // thread was never started
300 };
301
302 enum ebpf_global_table_values {
303 - NETDATA_EBPF_GLOBAL_TABLE_PID_TABLE_ADD, // Count elements added inside PID table
304 - NETDATA_EBPF_GLOBAL_TABLE_PID_TABLE_DEL, // Count elements removed from PID table
303 + NETDATA_EBPF_GLOBAL_TABLE_PID_TABLE_ADD, // Count elements added inside PID table
304 + NETDATA_EBPF_GLOBAL_TABLE_PID_TABLE_DEL, // Count elements removed from PID table
305 NETDATA_EBPF_GLOBAL_TABLE_TEMP_TABLE_ADD, // Count elements added inside TEMP table
306 - NETDATA_EBPF_GLOBAL_TABLE_TEMP_TABLE_DEL, // Count elements removed from TEMP table
306 + NETDATA_EBPF_GLOBAL_TABLE_TEMP_TABLE_DEL, // Count elements removed from TEMP table
307
308 NETDATA_EBPF_GLOBAL_TABLE_STATUS_END
309 };
@@ -320,11 +320,12 @@ typedef struct ebpf_module {
320
321 // Helpers used with plugin
322 struct {
323 - void (*start_routine)(void *); // the thread function
324 - void (*apps_routine)(struct ebpf_module *em, void *ptr); // the apps charts
325 - void (*fnct_routine)(BUFFER *bf, struct ebpf_module *em); // the function used for exteernal requests
326 - const char *fcnt_name; // name given to cloud
327 - const char *fcnt_desc; // description given about function
323 + void (*start_routine)(void *); // the thread function
324 + void (*apps_routine)(struct ebpf_module *em, void *ptr); // the apps charts
325 + void (*fnct_routine)(BUFFER *bf, struct ebpf_module *em); // the function used for exteernal requests
326 + void (*bpf_unload)(struct ebpf_module *em); // BPF teardown, called from the module's own cleanup function on normal (non-shutdown) exit
327 + const char *fcnt_name; // name given to cloud
328 + const char *fcnt_desc; // description given about function
329 const char *fcnt_thread_chart_name;
330 int order_thread_chart;
331 const char *fcnt_thread_lifetime_name;
@@ -371,8 +372,8 @@ typedef struct ebpf_module {
372 int ebpf_get_kernel_version();
373 int get_redhat_release();
374 char *ebpf_kernel_suffix(int version, int isrh);
374 -struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kver, int is_rhf,
375 - struct bpf_object **obj);
375 +struct bpf_link **
376 +ebpf_load_program(char *plugins_dir, ebpf_module_t *em, int kver, int is_rhf, struct bpf_object **obj);
377
378 void ebpf_mount_config_name(char *filename, size_t length, char *path, const char *config);
379 int ebpf_load_config(struct config *config, char *filename);
@@ -461,9 +462,9 @@ void ebpf_histogram_dimension_cleanup(char **ptr, size_t length);
462
463 // Tracepoint helpers
464 // For more information related to tracepoints read https://www.kernel.org/doc/html/latest/trace/tracepoints.html
464 -int ebpf_is_tracepoint_enabled(char *subsys, char *eventname);
465 -int ebpf_enable_tracing_values(char *subsys, char *eventname);
466 -int ebpf_disable_tracing_values(char *subsys, char *eventname);
465 +int ebpf_is_tracepoint_enabled(const char *subsys, const char *eventname);
466 +int ebpf_enable_tracing_values(const char *subsys, const char *eventname);
467 +int ebpf_disable_tracing_values(const char *subsys, const char *eventname);
468
469 // BTF Section
470 #define EBPF_DEFAULT_BTF_FILE "vmlinux"
@@ -485,8 +486,10 @@ void ebpf_update_map_type(struct bpf_map *map, ebpf_local_maps_t *w);
486 void ebpf_define_map_type(ebpf_local_maps_t *maps, int maps_per_core, int kver);
487 #endif
488
488 -void ebpf_update_kernel_memory_with_vector(ebpf_plugin_stats_t *report, ebpf_local_maps_t *maps,
489 - ebpf_stats_action_t action);
489 +void ebpf_update_kernel_memory_with_vector(
490 + ebpf_plugin_stats_t *report,
491 + ebpf_local_maps_t *maps,
492 + ebpf_stats_action_t action);
493 void ebpf_update_kernel_memory(ebpf_plugin_stats_t *report, ebpf_local_maps_t *map, ebpf_stats_action_t action);
494 int ebpf_statistic_create_aral_chart(char *name, ebpf_module_t *em);
495 void ebpf_statistic_obsolete_aral_chart(ebpf_module_t *em, int prio);
@@ -496,11 +499,12 @@ int ebpf_can_plugin_load_code(int kver, char *plugin_name);
499 int ebpf_adjust_memory_limit();
500
501 #ifdef LIBBPF_MAJOR_VERSION
499 -static inline int netdata_silent_libbpf_vfprintf(enum libbpf_print_level level __maybe_unused,
500 - const char *format __maybe_unused,
501 - va_list args __maybe_unused)
502 +static inline int netdata_silent_libbpf_vfprintf(
503 + enum libbpf_print_level level __maybe_unused,
504 + const char *format __maybe_unused,
505 + va_list args __maybe_unused)
506 {
503 - return 0;
507 + return 0;
508 }
509 #endif
510
src/collectors/ebpf.plugin/libbpf_api/ebpf_library.c new
+1889
@@ -0,0 +1,1889 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include <stdio.h>
4 +#include <stdlib.h>
5 +#include <pthread.h>
6 +
7 +#include "libnetdata/libnetdata.h"
8 +#include "ebpf_library.h"
9 +#include "../ebpf.h"
10 +#include "../ebpf_process.h"
11 +#include "../ebpf_socket.h"
12 +#include <ifaddrs.h>
13 +
14 +/*****************************************************************
15 + *
16 + * DIMENSION WRITING FUNCTIONS
17 + *
18 + *****************************************************************/
19 +
20 +void write_chart_dimension(const char *dim, long long value)
21 +{
22 + printf("SET %s = %lld\n", dim, value);
23 +}
24 +
25 +void ebpf_write_global_dimension(char *name, char *id, char *algorithm)
26 +{
27 + printf("DIMENSION %s %s %s 1 1\n", name, id, algorithm);
28 +}
29 +
30 +void ebpf_create_global_dimension(void *ptr, int end)
31 +{
32 + netdata_publish_syscall_t *move = ptr;
33 +
34 + int i = 0;
35 + while (move && i < end) {
36 + ebpf_write_global_dimension(move->name, move->dimension, move->algorithm);
37 +
38 + move = move->next;
39 + i++;
40 + }
41 +}
42 +
43 +/*****************************************************************
44 + *
45 + * CHART WRITING FUNCTIONS
46 + *
47 + *****************************************************************/
48 +
49 +void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end)
50 +{
51 + ebpf_write_begin_chart(family, name, "");
52 +
53 + uint32_t i;
54 + for (i = 0; move && i < end; i++) {
55 + write_chart_dimension(move->name, move->ncall);
56 + move = move->next;
57 + }
58 +
59 + ebpf_write_end_chart();
60 +}
61 +
62 +void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end)
63 +{
64 + ebpf_write_begin_chart(family, name, "");
65 +
66 + int i;
67 + for (i = 0; move && i < end; i++) {
68 + write_chart_dimension(move->name, move->nerr);
69 + move = move->next;
70 + }
71 +
72 + ebpf_write_end_chart();
73 +}
74 +
75 +void ebpf_one_dimension_write_charts(char *family, char *chart, char *dim, long long v1)
76 +{
77 + ebpf_write_begin_chart(family, chart, "");
78 +
79 + write_chart_dimension(dim, v1);
80 +
81 + ebpf_write_end_chart();
82 +}
83 +
84 +void write_io_chart(char *chart, char *family, char *dwrite, long long vwrite, char *dread, long long vread)
85 +{
86 + ebpf_write_begin_chart(family, chart, "");
87 +
88 + write_chart_dimension(dwrite, vwrite);
89 + write_chart_dimension(dread, vread);
90 +
91 + ebpf_write_end_chart();
92 +}
93 +
94 +void write_histogram_chart(char *family, char *name, const uint64_t *hist, char **dimensions, uint32_t end)
95 +{
96 + ebpf_write_begin_chart(family, name, "");
97 +
98 + uint32_t i;
99 + for (i = 0; i < end; i++) {
100 + write_chart_dimension(dimensions[i], (long long)hist[i]);
101 + }
102 +
103 + ebpf_write_end_chart();
104 +
105 + fflush(stdout);
106 +}
107 +
108 +/*****************************************************************
109 + *
110 + * CHART CREATION FUNCTIONS
111 + *
112 + *****************************************************************/
113 +
114 +void ebpf_write_chart_cmd(
115 + char *type,
116 + char *id,
117 + char *suffix,
118 + char *title,
119 + char *units,
120 + char *family,
121 + char *charttype,
122 + char *context,
123 + int order,
124 + int update_every,
125 + char *module)
126 +{
127 + printf(
128 + "CHART %s.%s%s '' '%s' '%s' '%s' '%s' '%s' %d %d '' 'ebpf.plugin' '%s'\n",
129 + type,
130 + id,
131 + suffix,
132 + title,
133 + units,
134 + (family) ? family : "",
135 + (context) ? context : "",
136 + (charttype) ? charttype : "",
137 + order,
138 + update_every,
139 + module);
140 +}
141 +
142 +void ebpf_write_chart_obsolete(
143 + char *type,
144 + const char *id,
145 + char *suffix,
146 + char *title,
147 + char *units,
148 + char *family,
149 + char *charttype,
150 + const char *context,
151 + int order,
152 + int update_every)
153 +{
154 + printf(
155 + "CHART %s.%s%s '' '%s' '%s' '%s' '%s' '%s' %d %d 'obsolete'\n",
156 + type,
157 + id,
158 + suffix,
159 + title,
160 + units,
161 + (family) ? family : "",
162 + (context) ? context : "",
163 + (charttype) ? charttype : "",
164 + order,
165 + update_every);
166 +}
167 +
168 +void ebpf_create_chart(
169 + char *type,
170 + char *id,
171 + char *title,
172 + char *units,
173 + char *family,
174 + char *context,
175 + char *charttype,
176 + int order,
177 + void (*ncd)(void *, int),
178 + void *move,
179 + int end,
180 + int update_every,
181 + char *module)
182 +{
183 + ebpf_write_chart_cmd(type, id, "", title, units, family, charttype, context, order, update_every, module);
184 +
185 + if (ncd) {
186 + ncd(move, end);
187 + }
188 +}
189 +
190 +/*****************************************************************
191 + *
192 + * ARAL STATISTIC CHARTS
193 + *
194 + *****************************************************************/
195 +
196 +int ebpf_statistic_create_aral_chart(char *name, ebpf_module_t *em)
197 +{
198 + static int priority = NETDATA_EBPF_ORDER_STAT_ARAL_BEGIN;
199 + static netdata_mutex_t priority_mutex;
200 + static int priority_mutex_initialized = 0;
201 +
202 + if (!priority_mutex_initialized) {
203 + netdata_mutex_init(&priority_mutex);
204 + priority_mutex_initialized = 1;
205 + }
206 +
207 + char *mem = NETDATA_EBPF_STAT_DIMENSION_MEMORY;
208 + char *aral = NETDATA_EBPF_STAT_DIMENSION_ARAL;
209 +
210 + snprintfz(em->memory_usage, NETDATA_EBPF_CHART_MEM_LENGTH - 1, "aral_%s_size", name);
211 + snprintfz(em->memory_allocations, NETDATA_EBPF_CHART_MEM_LENGTH - 1, "aral_%s_alloc", name);
212 +
213 + netdata_mutex_lock(&priority_mutex);
214 + int ret_priority = priority;
215 + priority += 2;
216 + netdata_mutex_unlock(&priority_mutex);
217 +
218 + ebpf_write_chart_cmd(
219 + NETDATA_MONITORING_FAMILY,
220 + em->memory_usage,
221 + "",
222 + "Bytes allocated for ARAL.",
223 + "bytes",
224 + NETDATA_EBPF_FAMILY,
225 + NETDATA_EBPF_CHART_TYPE_STACKED,
226 + "netdata.ebpf_aral_stat_size",
227 + ret_priority,
228 + em->update_every,
229 + NETDATA_EBPF_MODULE_NAME_PROCESS);
230 +
231 + ebpf_write_global_dimension(mem, mem, ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
232 +
233 + ebpf_write_chart_cmd(
234 + NETDATA_MONITORING_FAMILY,
235 + em->memory_allocations,
236 + "",
237 + "Calls to allocate memory.",
238 + "calls",
239 + NETDATA_EBPF_FAMILY,
240 + NETDATA_EBPF_CHART_TYPE_STACKED,
241 + "netdata.ebpf_aral_stat_alloc",
242 + ret_priority + 1,
243 + em->update_every,
244 + NETDATA_EBPF_MODULE_NAME_PROCESS);
245 +
246 + ebpf_write_global_dimension(aral, aral, ebpf_algorithms[NETDATA_EBPF_ABSOLUTE_IDX]);
247 +
248 + return ret_priority;
249 +}
250 +
251 +void ebpf_statistic_obsolete_aral_chart(ebpf_module_t *em, int prio)
252 +{
253 + ebpf_write_chart_obsolete(
254 + NETDATA_MONITORING_FAMILY,
255 + em->memory_usage,
256 + "",
257 + "Bytes allocated for ARAL.",
258 + "bytes",
259 + NETDATA_EBPF_FAMILY,
260 + NETDATA_EBPF_CHART_TYPE_STACKED,
261 + "netdata.ebpf_aral_stat_size",
262 + prio++,
263 + em->update_every);
264 +
265 + ebpf_write_chart_obsolete(
266 + NETDATA_MONITORING_FAMILY,
267 + em->memory_allocations,
268 + "",
269 + "Calls to allocate memory.",
270 + "calls",
271 + NETDATA_EBPF_FAMILY,
272 + NETDATA_EBPF_CHART_TYPE_STACKED,
273 + "netdata.ebpf_aral_stat_alloc",
274 + prio++,
275 + em->update_every);
276 +}
277 +
278 +void ebpf_send_data_aral_chart(ARAL *memory, ebpf_module_t *em)
279 +{
280 + if (!memory)
281 + return;
282 +
283 + char *mem = NETDATA_EBPF_STAT_DIMENSION_MEMORY;
284 + char *aral = NETDATA_EBPF_STAT_DIMENSION_ARAL;
285 +
286 + struct aral_statistics *stats = aral_get_statistics(memory);
287 +
288 + ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, em->memory_usage, "");
289 + write_chart_dimension(mem, (long long)stats->structures.allocated_bytes);
290 + ebpf_write_end_chart();
291 +
292 + ebpf_write_begin_chart(NETDATA_MONITORING_FAMILY, em->memory_allocations, "");
293 + write_chart_dimension(aral, (long long)stats->structures.allocations);
294 + ebpf_write_end_chart();
295 +}
296 +
297 +/*****************************************************************
298 + *
299 + * CONFIG FILE PARSER FUNCTIONS
300 + *
301 + *****************************************************************/
302 +
303 +void ebpf_how_to_load(const char *ptr)
304 +{
305 + if (!strcasecmp(ptr, EBPF_CFG_LOAD_MODE_RETURN))
306 + ebpf_set_thread_mode(MODE_RETURN);
307 + else if (!strcasecmp(ptr, EBPF_CFG_LOAD_MODE_DEFAULT))
308 + ebpf_set_thread_mode(MODE_ENTRY);
309 + else
310 + netdata_log_error("the option %s for \"ebpf load mode\" is not a valid option.", ptr);
311 +}
312 +
313 +void ebpf_set_apps_mode(netdata_apps_integration_flags_t value)
314 +{
315 + int i;
316 + for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
317 + ebpf_modules[i].apps_charts = value;
318 + }
319 +}
320 +
321 +void ebpf_update_interval(int update_every)
322 +{
323 + int i;
324 +
325 + int value = (int)inicfg_get_number(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_UPDATE_EVERY, update_every);
326 +
327 + for (i = 0; ebpf_modules[i].info.thread_name; i++) {
328 + ebpf_modules[i].update_every = value;
329 + }
330 +}
331 +
332 +void ebpf_update_table_size()
333 +{
334 + uint32_t value = (uint32_t)inicfg_get_number(
335 + &collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, ND_EBPF_DEFAULT_PID_SIZE);
336 + for (int i = 0; ebpf_modules[i].info.thread_name; i++) {
337 + ebpf_modules[i].pid_map_size = value;
338 + }
339 +}
340 +
341 +void ebpf_update_lifetime()
342 +{
343 + uint32_t value =
344 + (uint32_t)inicfg_get_number(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_LIFETIME, EBPF_DEFAULT_LIFETIME);
345 +
346 + for (int i = 0; ebpf_modules[i].info.thread_name; i++) {
347 + ebpf_modules[i].lifetime = value;
348 + }
349 +}
350 +
351 +void ebpf_set_load_mode(netdata_ebpf_load_mode_t load, netdata_ebpf_load_mode_t origin)
352 +{
353 + int i;
354 + for (i = 0; ebpf_modules[i].info.thread_name; i++) {
355 + ebpf_modules[i].load &= ~NETDATA_EBPF_LOAD_METHODS;
356 + ebpf_modules[i].load |= load | origin;
357 + }
358 +}
359 +
360 +void ebpf_update_load_mode(const char *str, netdata_ebpf_load_mode_t origin)
361 +{
362 + netdata_ebpf_load_mode_t load = epbf_convert_string_to_load_mode(str);
363 +
364 + ebpf_set_load_mode(load, origin);
365 +}
366 +
367 +void ebpf_update_map_per_core()
368 +{
369 + int value = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_MAPS_PER_CORE, CONFIG_BOOLEAN_YES);
370 +
371 + for (int i = 0; ebpf_modules[i].info.thread_name; i++) {
372 + ebpf_modules[i].maps_per_core = value;
373 + }
374 +}
375 +
376 +void ebpf_set_ipc_value(const char *integration)
377 +{
378 + if (!strcmp(integration, NETDATA_EBPF_IPC_INTEGRATION_SHM))
379 + integration_with_collectors = NETDATA_EBPF_INTEGRATION_SHM;
380 + else if (!strcmp(integration, NETDATA_EBPF_IPC_INTEGRATION_SOCKET))
381 + integration_with_collectors = NETDATA_EBPF_INTEGRATION_SOCKET;
382 + else
383 + integration_with_collectors = NETDATA_EBPF_INTEGRATION_DISABLED;
384 +}
385 +
386 +void ebpf_parse_ipc_section()
387 +{
388 + const char *integration = inicfg_get(
389 + &collector_config,
390 + NETDATA_EBPF_IPC_SECTION,
391 + NETDATA_EBPF_IPC_INTEGRATION,
392 + NETDATA_EBPF_IPC_INTEGRATION_DISABLED);
393 + ebpf_set_ipc_value(integration);
394 +
395 + ipc_sockets.default_bind_to = inicfg_get(
396 + &collector_config, NETDATA_EBPF_IPC_SECTION, NETDATA_EBPF_IPC_BIND_TO, NETDATA_EBPF_IPC_BIND_TO_DEFAULT);
397 +
398 + ipc_sockets.backlog =
399 + (int)inicfg_get_number(&collector_config, NETDATA_EBPF_IPC_SECTION, NETDATA_EBPF_IPC_BACKLOG, 20);
400 +}
401 +
402 +void ebpf_set_thread_mode(netdata_run_mode_t lmode)
403 +{
404 + int i;
405 + for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
406 + ebpf_modules[i].mode = lmode;
407 + }
408 +}
409 +
410 +void ebpf_enable_specific_chart(ebpf_module_t *em, int disable_cgroup)
411 +{
412 + em->enabled = NETDATA_THREAD_EBPF_RUNNING;
413 +
414 + if (!disable_cgroup) {
415 + em->cgroup_charts = CONFIG_BOOLEAN_YES;
416 + }
417 +
418 + em->global_charts = CONFIG_BOOLEAN_YES;
419 +}
420 +
421 +void ebpf_enable_chart(int idx, int disable_cgroup)
422 +{
423 + int i;
424 + for (i = 0; ebpf_modules[i].info.thread_name; i++) {
425 + if (i == idx) {
426 + ebpf_enable_specific_chart(&ebpf_modules[i], disable_cgroup);
427 + break;
428 + }
429 + }
430 +}
431 +
432 +int ebpf_load_collector_config(char *path, int *disable_cgroups, int update_every)
433 +{
434 + char lpath[4096];
435 + netdata_ebpf_load_mode_t origin;
436 +
437 + snprintf(lpath, 4095, "%s/%s", path, NETDATA_EBPF_CONFIG_FILE);
438 + if (!inicfg_load(&collector_config, lpath, 0, NULL)) {
439 + snprintf(lpath, 4095, "%s/%s", path, NETDATA_EBPF_OLD_CONFIG_FILE);
440 + if (!inicfg_load(&collector_config, lpath, 0, NULL)) {
441 + return -1;
442 + }
443 + origin = EBPF_LOADED_FROM_STOCK;
444 + } else
445 + origin = EBPF_LOADED_FROM_USER;
446 +
447 + read_collector_values(disable_cgroups, update_every, origin);
448 + ebpf_parse_ipc_section();
449 +
450 + return 0;
451 +}
452 +
453 +void ebpf_load_thread_config()
454 +{
455 + int i;
456 + for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
457 + ebpf_update_module(&ebpf_modules[i], default_btf, running_on_kernel, isrh);
458 + }
459 +}
460 +
461 +void read_collector_values(int *disable_cgroups, int update_every, netdata_ebpf_load_mode_t origin)
462 +{
463 + const char *value;
464 + if (inicfg_exists(&collector_config, EBPF_GLOBAL_SECTION, "load"))
465 + value = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, "load", EBPF_CFG_LOAD_MODE_DEFAULT);
466 + else
467 + value = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_LOAD_MODE, EBPF_CFG_LOAD_MODE_DEFAULT);
468 +
469 + ebpf_how_to_load(value);
470 +
471 + btf_path = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_PROGRAM_PATH, EBPF_DEFAULT_BTF_PATH);
472 +
473 +#ifdef LIBBPF_MAJOR_VERSION
474 + default_btf = ebpf_load_btf_file(btf_path, EBPF_DEFAULT_BTF_FILE);
475 +#endif
476 +
477 + value = inicfg_get(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_TYPE_FORMAT, EBPF_CFG_DEFAULT_PROGRAM);
478 +
479 + ebpf_update_load_mode(value, origin);
480 +
481 + ebpf_update_interval(update_every);
482 +
483 + ebpf_update_table_size();
484 +
485 + ebpf_update_lifetime();
486 +
487 + uint32_t enabled = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, "disable apps", CONFIG_BOOLEAN_NO);
488 + if (!enabled) {
489 + // `application` is a positive option, but the legacy `disable apps`
490 + // setting is negative. Preserve the original compatibility semantics.
491 + enabled = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_APPLICATION, CONFIG_BOOLEAN_YES);
492 + enabled = (enabled == CONFIG_BOOLEAN_NO) ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
493 + }
494 +
495 + ebpf_set_apps_mode(!enabled);
496 +
497 + enabled = inicfg_get_boolean(&collector_config, EBPF_GLOBAL_SECTION, EBPF_CFG_CGROUP, CONFIG_BOOLEAN_NO);
498 + *disable_cgroups = (enabled == CONFIG_BOOLEAN_NO) ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO;
499 +
500 + ebpf_update_map_per_core();
501 +
502 + enabled = inicfg_get_boolean(
503 + &collector_config,
504 + EBPF_PROGRAMS_SECTION,
505 + ebpf_modules[EBPF_MODULE_PROCESS_IDX].info.config_name,
506 + CONFIG_BOOLEAN_YES);
507 + if (enabled) {
508 + ebpf_enable_chart(EBPF_MODULE_PROCESS_IDX, *disable_cgroups);
509 + }
510 +
511 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "network viewer", CONFIG_BOOLEAN_NO);
512 + if (!enabled)
513 + enabled = inicfg_get_boolean(
514 + &collector_config,
515 + EBPF_PROGRAMS_SECTION,
516 + ebpf_modules[EBPF_MODULE_SOCKET_IDX].info.config_name,
517 + CONFIG_BOOLEAN_NO);
518 + if (enabled) {
519 + ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_cgroups);
520 + }
521 +
522 + enabled = inicfg_get_boolean(
523 + &collector_config, EBPF_PROGRAMS_SECTION, "network connection monitoring", CONFIG_BOOLEAN_YES);
524 + if (!enabled)
525 + enabled =
526 + inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "network connections", CONFIG_BOOLEAN_YES);
527 +
528 + network_viewer_opt.enabled = enabled;
529 + if (enabled) {
530 + if (!ebpf_modules[EBPF_MODULE_SOCKET_IDX].enabled)
531 + ebpf_enable_chart(EBPF_MODULE_SOCKET_IDX, *disable_cgroups);
532 +
533 + parse_network_viewer_section(&collector_config);
534 + ebpf_parse_service_name_section(&collector_config);
535 + }
536 +
537 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "cachestat", CONFIG_BOOLEAN_NO);
538 + if (enabled) {
539 + ebpf_enable_chart(EBPF_MODULE_CACHESTAT_IDX, *disable_cgroups);
540 + }
541 +
542 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "sync", CONFIG_BOOLEAN_YES);
543 + if (enabled) {
544 + ebpf_enable_chart(EBPF_MODULE_SYNC_IDX, *disable_cgroups);
545 + }
546 +
547 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "dcstat", CONFIG_BOOLEAN_NO);
548 + if (enabled) {
549 + ebpf_enable_chart(EBPF_MODULE_DCSTAT_IDX, *disable_cgroups);
550 + }
551 +
552 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "swap", CONFIG_BOOLEAN_NO);
553 + if (enabled) {
554 + ebpf_enable_chart(EBPF_MODULE_SWAP_IDX, *disable_cgroups);
555 + }
556 +
557 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "vfs", CONFIG_BOOLEAN_NO);
558 + if (enabled) {
559 + ebpf_enable_chart(EBPF_MODULE_VFS_IDX, *disable_cgroups);
560 + }
561 +
562 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "filesystem", CONFIG_BOOLEAN_NO);
563 + if (enabled) {
564 + ebpf_enable_chart(EBPF_MODULE_FILESYSTEM_IDX, *disable_cgroups);
565 + }
566 +
567 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "disk", CONFIG_BOOLEAN_NO);
568 + if (enabled) {
569 + ebpf_enable_chart(EBPF_MODULE_DISK_IDX, *disable_cgroups);
570 + }
571 +
572 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "mount", CONFIG_BOOLEAN_YES);
573 + if (enabled) {
574 + ebpf_enable_chart(EBPF_MODULE_MOUNT_IDX, *disable_cgroups);
575 + }
576 +
577 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "fd", CONFIG_BOOLEAN_YES);
578 + if (enabled) {
579 + ebpf_enable_chart(EBPF_MODULE_FD_IDX, *disable_cgroups);
580 + }
581 +
582 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "hardirq", CONFIG_BOOLEAN_YES);
583 + if (enabled) {
584 + ebpf_enable_chart(EBPF_MODULE_HARDIRQ_IDX, *disable_cgroups);
585 + }
586 +
587 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "softirq", CONFIG_BOOLEAN_YES);
588 + if (enabled) {
589 + ebpf_enable_chart(EBPF_MODULE_SOFTIRQ_IDX, *disable_cgroups);
590 + }
591 +
592 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "oomkill", CONFIG_BOOLEAN_YES);
593 + if (enabled) {
594 + ebpf_enable_chart(EBPF_MODULE_OOMKILL_IDX, *disable_cgroups);
595 + }
596 +
597 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "shm", CONFIG_BOOLEAN_YES);
598 + if (enabled) {
599 + ebpf_enable_chart(EBPF_MODULE_SHM_IDX, *disable_cgroups);
600 + }
601 +
602 + enabled = inicfg_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "mdflush", CONFIG_BOOLEAN_NO);
603 + if (enabled) {
604 + ebpf_enable_chart(EBPF_MODULE_MDFLUSH_IDX, *disable_cgroups);
605 + }
606 +}
607 +
608 +/**
609 + * Link hostname
610 + *
611 + * @param out is the output link list
612 + * @param in the hostname to add to list.
613 + */
614 +static void ebpf_link_hostname(ebpf_network_viewer_hostname_list_t **out, ebpf_network_viewer_hostname_list_t *in)
615 +{
616 + if (likely(*out)) {
617 + ebpf_network_viewer_hostname_list_t *move = *out;
618 + for (; move->next; move = move->next) {
619 + if (move->hash == in->hash && !strcmp(move->value, in->value)) {
620 + netdata_log_info("The hostname %s was already inserted, it will be ignored.", in->value);
621 + freez(in->value);
622 + simple_pattern_free(in->value_pattern);
623 + freez(in);
624 + return;
625 + }
626 + }
627 +
628 + move->next = in;
629 + } else {
630 + *out = in;
631 + }
632 +#ifdef NETDATA_INTERNAL_CHECKS
633 + netdata_log_info(
634 + "Adding value %s to %s hostname list used on network viewer",
635 + in->value,
636 + (*out == network_viewer_opt.included_hostnames) ? "included" : "excluded");
637 +#endif
638 +}
639 +
640 +/**
641 + * Link Hostnames
642 + *
643 + * Parse the list of hostnames to create the link list.
644 + * This is not associated with the IP, because simple patterns like *example* cannot be resolved to IP.
645 + *
646 + * @param out is the output link list
647 + * @param parse is a pointer with the text to parser.
648 + */
649 +static void ebpf_link_hostnames(const char *parse)
650 +{
651 + // No value
652 + if (unlikely(!parse))
653 + return;
654 +
655 + char *move = strdupz(parse);
656 + char *clean = move;
657 + while (likely(move)) {
658 + // Find the first valid value
659 + while (isspace(*move))
660 + move++;
661 +
662 + // No valid value found
663 + if (unlikely(!*move)) {
664 + freez(clean);
665 + return;
666 + }
667 +
668 + // Find space that ends the list
669 + char *end = strchr(move, ' ');
670 + if (end) {
671 + *end++ = '\0';
672 + }
673 +
674 + bool neg = false;
675 + if (*move == '!') {
676 + neg = true;
677 + move++;
678 + }
679 +
680 + ebpf_network_viewer_hostname_list_t *hostname = callocz(1, sizeof(ebpf_network_viewer_hostname_list_t));
681 + hostname->value = strdupz(move);
682 + hostname->hash = simple_hash(move);
683 + hostname->value_pattern = simple_pattern_create(move, NULL, SIMPLE_PATTERN_EXACT, true);
684 +
685 + ebpf_link_hostname(
686 + (!neg) ? &network_viewer_opt.included_hostnames : &network_viewer_opt.excluded_hostnames, hostname);
687 +
688 + move = end;
689 + }
690 + freez(clean);
691 +}
692 +
693 +void parse_network_viewer_section(struct config *cfg)
694 +{
695 + network_viewer_opt.hostname_resolution_enabled =
696 + inicfg_get_boolean(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_RESOLVE_HOSTNAME, CONFIG_BOOLEAN_NO);
697 +
698 + network_viewer_opt.service_resolution_enabled =
699 + inicfg_get_boolean(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_RESOLVE_SERVICE, CONFIG_BOOLEAN_YES);
700 +
701 + const char *value = inicfg_get(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_PORTS, NULL);
702 + ebpf_parse_ports(value);
703 +
704 + if (network_viewer_opt.hostname_resolution_enabled) {
705 + value = inicfg_get(cfg, EBPF_NETWORK_VIEWER_SECTION, EBPF_CONFIG_HOSTNAMES, NULL);
706 + ebpf_link_hostnames(value);
707 + } else {
708 + netdata_log_info("Name resolution is disabled, collector will not parse \"hostnames\" list.");
709 + }
710 +
711 + value = inicfg_get(cfg, EBPF_NETWORK_VIEWER_SECTION, "ips", NULL);
712 + ebpf_parse_ips_unsafe(value);
713 +}
714 +
715 +/*****************************************************************
716 + *
717 + * IP PARSING FUNCTIONS
718 + *
719 + *****************************************************************/
720 +
721 +/**
722 + * Netmask
723 + *
724 + * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
725 + *
726 + * @param prefix create the netmask based in the CIDR value.
727 + *
728 + * @return
729 + */
730 +static inline in_addr_t ebpf_netmask(int prefix)
731 +{
732 + if (prefix == 0)
733 + return (~((in_addr_t)-1));
734 + else
735 + return (in_addr_t)(~((1 << (32 - prefix)) - 1));
736 +}
737 +
738 +/**
739 + * Broadcast
740 + *
741 + * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
742 + *
743 + * @param addr is the ip address
744 + * @param prefix is the CIDR value.
745 + *
746 + * @return It returns the last address of the range
747 + */
748 +static inline in_addr_t ebpf_broadcast(in_addr_t addr, int prefix)
749 +{
750 + return (addr | ~ebpf_netmask(prefix));
751 +}
752 +
753 +/**
754 + * Network
755 + *
756 + * Copied from iprange (https://github.com/firehol/iprange/blob/master/iprange.h)
757 + *
758 + * @param addr is the ip address
759 + * @param prefix is the CIDR value.
760 + *
761 + * @return It returns the first address of the range.
762 + */
763 +static inline in_addr_t ebpf_ipv4_network(in_addr_t addr, int prefix)
764 +{
765 + return (addr & ebpf_netmask(prefix));
766 +}
767 +
768 +/**
769 + * Calculate ipv6 first address
770 + *
771 + * @param out the address to store the first address.
772 + * @param in the address used to do the math.
773 + * @param prefix number of bits used to calculate the address
774 + */
775 +static void get_ipv6_first_addr(union netdata_ip_t *out, union netdata_ip_t *in, uint64_t prefix)
776 +{
777 + uint64_t mask, tmp;
778 + uint64_t ret[2];
779 +
780 + memcpy(ret, in->addr32, sizeof(union netdata_ip_t));
781 +
782 + if (prefix == 128) {
783 + memcpy(out->addr32, in->addr32, sizeof(union netdata_ip_t));
784 + return;
785 + } else if (!prefix) {
786 + ret[0] = ret[1] = 0;
787 + memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
788 + return;
789 + } else if (prefix <= 64) {
790 + ret[1] = 0ULL;
791 +
792 + tmp = be64toh(ret[0]);
793 + mask = 0xFFFFFFFFFFFFFFFFULL << (64 - prefix);
794 + tmp &= mask;
795 + ret[0] = htobe64(tmp);
796 + } else {
797 + mask = 0xFFFFFFFFFFFFFFFFULL << (128 - prefix);
798 + tmp = be64toh(ret[1]);
799 + tmp &= mask;
800 + ret[1] = htobe64(tmp);
801 + }
802 +
803 + memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
804 +}
805 +
806 +/**
807 + * Get IPV6 Last Address
808 + *
809 + * @param out the address to store the last address.
810 + * @param in the address used to do the math.
811 + * @param prefix number of bits used to calculate the address
812 + */
813 +static void get_ipv6_last_addr(union netdata_ip_t *out, union netdata_ip_t *in, uint64_t prefix)
814 +{
815 + uint64_t mask, tmp;
816 + uint64_t ret[2];
817 + memcpy(ret, in->addr32, sizeof(union netdata_ip_t));
818 +
819 + if (prefix == 128) {
820 + memcpy(out->addr32, in->addr32, sizeof(union netdata_ip_t));
821 + return;
822 + } else if (!prefix) {
823 + ret[0] = ret[1] = 0xFFFFFFFFFFFFFFFF;
824 + memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
825 + return;
826 + } else if (prefix <= 64) {
827 + ret[1] = 0xFFFFFFFFFFFFFFFFULL;
828 +
829 + tmp = be64toh(ret[0]);
830 + mask = 0xFFFFFFFFFFFFFFFFULL << (64 - prefix);
831 + tmp |= ~mask;
832 + ret[0] = htobe64(tmp);
833 + } else {
834 + mask = 0xFFFFFFFFFFFFFFFFULL << (128 - prefix);
835 + tmp = be64toh(ret[1]);
836 + tmp |= ~mask;
837 + ret[1] = htobe64(tmp);
838 + }
839 +
840 + memcpy(out->addr32, ret, sizeof(union netdata_ip_t));
841 +}
842 +
843 +/**
844 + * IP to network long
845 + *
846 + * @param dst the vector to store the result
847 + * @param ip the source ip given by our users.
848 + * @param domain the ip domain (IPV4 or IPV6)
849 + * @param source the original string
850 + *
851 + * @return it returns 0 on success and -1 otherwise.
852 + */
853 +static inline int ebpf_ip2nl(uint8_t *dst, const char *ip, int domain, char *source)
854 +{
855 + if (inet_pton(domain, ip, dst) <= 0) {
856 + netdata_log_error("The address specified (%s) is invalid ", source);
857 + return -1;
858 + }
859 +
860 + return 0;
861 +}
862 +
863 +/**
864 + * Clean IP structure
865 + *
866 + * Clean the allocated list.
867 + *
868 + * @param clean the list that will be cleaned
869 + */
870 +void ebpf_clean_ip_structure(ebpf_network_viewer_ip_list_t **clean)
871 +{
872 + ebpf_network_viewer_ip_list_t *move = *clean;
873 + while (move) {
874 + ebpf_network_viewer_ip_list_t *next = move->next;
875 + freez(move->value);
876 + freez(move);
877 + move = next;
878 + }
879 + *clean = NULL;
880 +}
881 +
882 +/**
883 + * Clean port structure
884 + *
885 + * Clean the allocated list.
886 + *
887 + * @param clean the list that will be cleaned
888 + */
889 +void ebpf_clean_port_structure(ebpf_network_viewer_port_list_t **clean)
890 +{
891 + ebpf_network_viewer_port_list_t *move = *clean;
892 + while (move) {
893 + ebpf_network_viewer_port_list_t *next = move->next;
894 + freez(move->value);
895 + freez(move);
896 + move = next;
897 + }
898 + *clean = NULL;
899 +}
900 +
901 +/**
902 + * Parse IP List
903 + *
904 + * Parse IP list and link it.
905 + *
906 + * @param out a pointer to store the link list
907 + * @param ip the value given as parameter
908 + */
909 +static void ebpf_parse_ip_list_unsafe(void **out, const char *ip)
910 +{
911 + ebpf_network_viewer_ip_list_t **list = (ebpf_network_viewer_ip_list_t **)out;
912 +
913 + char *ipdup = strdupz(ip);
914 + union netdata_ip_t first = {};
915 + union netdata_ip_t last = {};
916 + const char *is_ipv6;
917 + if (*ip == '*' && *(ip + 1) == '\0') {
918 + memset(first.addr8, 0, sizeof(first.addr8));
919 + memset(last.addr8, 0xFF, sizeof(last.addr8));
920 +
921 + is_ipv6 = ip;
922 +
923 + ebpf_clean_ip_structure(list);
924 + goto storethisip;
925 + }
926 +
927 + char *end = strdupz(ip);
928 + char *clean_end = end;
929 + // Move while I cannot find a separator
930 + while (*end && *end != '/' && *end != '-')
931 + end++;
932 +
933 + // We will use only the classic IPV6 for while, but we could consider the base 85 in a near future
934 + // https://tools.ietf.org/html/rfc1924
935 + is_ipv6 = strchr(ip, ':');
936 +
937 + int select;
938 + if (*end && !is_ipv6) { // IPV4 range
939 + select = (*end == '/') ? 0 : 1;
940 + *end++ = '\0';
941 + if (*end == '!') {
942 + netdata_log_info("The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
943 + goto cleanipdup;
944 + }
945 +
946 + if (!select) { // CIDR
947 + select = ebpf_ip2nl(first.addr8, ip, AF_INET, ipdup);
948 + if (select)
949 + goto cleanipdup;
950 +
951 + select = (int)str2i(end);
952 + if (select < NETDATA_MINIMUM_IPV4_CIDR || select > NETDATA_MAXIMUM_IPV4_CIDR) {
953 + netdata_log_info("The specified CIDR %s is not valid, the IP %s will be ignored.", end, ip);
954 + goto cleanipdup;
955 + }
956 +
957 + uint32_t ipv4_test = htonl(ebpf_ipv4_network(ntohl(first.addr32[0]), select));
958 + if (first.addr32[0] != ipv4_test) {
959 + first.addr32[0] = ipv4_test;
960 + struct in_addr ipv4_convert;
961 + ipv4_convert.s_addr = ipv4_test;
962 + char ipv4_msg[INET_ADDRSTRLEN];
963 + if (inet_ntop(AF_INET, &ipv4_convert, ipv4_msg, INET_ADDRSTRLEN))
964 + netdata_log_info("The network value of CIDR %s was updated for %s .", ipdup, ipv4_msg);
965 + }
966 +
967 + last.addr32[0] = htonl(ebpf_broadcast(ntohl(first.addr32[0]), select));
968 + } else { // Range
969 + select = ebpf_ip2nl(first.addr8, ip, AF_INET, ipdup);
970 + if (select)
971 + goto cleanipdup;
972 +
973 + select = ebpf_ip2nl(last.addr8, end, AF_INET, ipdup);
974 + if (select)
975 + goto cleanipdup;
976 + }
977 +
978 + if (ntohl(first.addr32[0]) > ntohl(last.addr32[0])) {
979 + netdata_log_info(
980 + "The specified range %s is invalid, the second address is smallest than the first, it will be ignored.",
981 + ipdup);
982 + goto cleanipdup;
983 + }
984 + } else if (is_ipv6) { // IPV6
985 + if (!*end) { // Unique
986 + select = ebpf_ip2nl(first.addr8, ip, AF_INET6, ipdup);
987 + if (select)
988 + goto cleanipdup;
989 +
990 + memcpy(last.addr8, first.addr8, sizeof(first.addr8));
991 + } else if (*end == '-') {
992 + *end++ = 0x00;
993 + if (*end == '!') {
994 + netdata_log_info(
995 + "The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
996 + goto cleanipdup;
997 + }
998 +
999 + select = ebpf_ip2nl(first.addr8, ip, AF_INET6, ipdup);
1000 + if (select)
1001 + goto cleanipdup;
1002 +
1003 + select = ebpf_ip2nl(last.addr8, end, AF_INET6, ipdup);
1004 + if (select)
1005 + goto cleanipdup;
1006 + } else { // CIDR
1007 + *end++ = 0x00;
1008 + if (*end == '!') {
1009 + netdata_log_info(
1010 + "The exclusion cannot be in the second part of the range %s, it will be ignored.", ipdup);
1011 + goto cleanipdup;
1012 + }
1013 +
1014 + select = str2i(end);
1015 + if (select < 0 || select > 128) {
1016 + netdata_log_info("The CIDR %s is not valid, the address %s will be ignored.", end, ip);
1017 + goto cleanipdup;
1018 + }
1019 +
1020 + uint64_t prefix = (uint64_t)select;
1021 + select = ebpf_ip2nl(first.addr8, ip, AF_INET6, ipdup);
1022 + if (select)
1023 + goto cleanipdup;
1024 +
1025 + get_ipv6_last_addr(&last, &first, prefix);
1026 +
1027 + union netdata_ip_t ipv6_test;
1028 + get_ipv6_first_addr(&ipv6_test, &first, prefix);
1029 +
1030 + if (memcmp(first.addr8, ipv6_test.addr8, sizeof(union netdata_ip_t)) != 0) {
1031 + memcpy(first.addr8, ipv6_test.addr8, sizeof(union netdata_ip_t));
1032 +
1033 + struct in6_addr ipv6_convert;
1034 + memcpy(ipv6_convert.s6_addr, ipv6_test.addr8, sizeof(union netdata_ip_t));
1035 +
1036 + char ipv6_msg[INET6_ADDRSTRLEN];
1037 + if (inet_ntop(AF_INET6, &ipv6_convert, ipv6_msg, INET6_ADDRSTRLEN))
1038 + netdata_log_info("The network value of CIDR %s was updated for %s .", ipdup, ipv6_msg);
1039 + }
1040 + }
1041 +
1042 + if ((be64toh(*(uint64_t *)&first.addr64[1]) > be64toh(*(uint64_t *)&last.addr64[1]) &&
1043 + memcmp(first.addr64, last.addr64, sizeof(uint64_t)) == 0) ||
1044 + (be64toh(*(uint64_t *)&first.addr64) > be64toh(*(uint64_t *)&last.addr64))) {
1045 + netdata_log_info(
1046 + "The specified range %s is invalid, the second address is smallest than the first, it will be ignored.",
1047 + ipdup);
1048 + goto cleanipdup;
1049 + }
1050 + } else { // Unique ip
1051 + select = ebpf_ip2nl(first.addr8, ip, AF_INET, ipdup);
1052 + if (select)
1053 + goto cleanipdup;
1054 +
1055 + memcpy(last.addr8, first.addr8, sizeof(first.addr8));
1056 + }
1057 +
1058 + ebpf_network_viewer_ip_list_t *store;
1059 +
1060 +storethisip:
1061 + store = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
1062 + store->value = ipdup;
1063 + store->hash = simple_hash(ipdup);
1064 + store->ver = (uint8_t)(!is_ipv6) ? AF_INET : AF_INET6;
1065 + memcpy(store->first.addr8, first.addr8, sizeof(first.addr8));
1066 + memcpy(store->last.addr8, last.addr8, sizeof(last.addr8));
1067 +
1068 + ebpf_fill_ip_list_unsafe(list, store, "socket");
1069 + return;
1070 +
1071 +cleanipdup:
1072 + freez(ipdup);
1073 + freez(clean_end);
1074 +}
1075 +
1076 +/**
1077 + * Check if the ip is inside a IP range
1078 + *
1079 + * @param rfirst the first ip address of the range
1080 + * @param rlast the last ip address of the range
1081 + * @param cmpfirst the first ip to compare
1082 + * @param cmplast the last ip to compare
1083 + * @param family the IP family
1084 + *
1085 + * @return It returns 1 if the IP is inside the range and 0 otherwise
1086 + */
1087 +static int ebpf_is_ip_inside_range(
1088 + union netdata_ip_t *rfirst,
1089 + union netdata_ip_t *rlast,
1090 + union netdata_ip_t *cmpfirst,
1091 + union netdata_ip_t *cmplast,
1092 + int family)
1093 +{
1094 + if (family == AF_INET) {
1095 + if ((rfirst->addr32[0] <= cmpfirst->addr32[0]) && (rlast->addr32[0] >= cmplast->addr32[0]))
1096 + return 1;
1097 + } else {
1098 + if (memcmp(rfirst->addr8, cmpfirst->addr8, sizeof(union netdata_ip_t)) <= 0 &&
1099 + memcmp(rlast->addr8, cmplast->addr8, sizeof(union netdata_ip_t)) >= 0) {
1100 + return 1;
1101 + }
1102 + }
1103 + return 0;
1104 +}
1105 +
1106 +/**
1107 + * Fill IP list
1108 + *
1109 + * @param out a pointer to link list.
1110 + * @param in the structure that will be linked.
1111 + * @param table the modified table.
1112 + */
1113 +void ebpf_fill_ip_list_unsafe(
1114 + ebpf_network_viewer_ip_list_t **out,
1115 + ebpf_network_viewer_ip_list_t *in,
1116 + char *table __maybe_unused)
1117 +{
1118 + if (in->ver == AF_INET) {
1119 + in->first.addr32[0] = ntohl(in->first.addr32[0]);
1120 + in->last.addr32[0] = ntohl(in->last.addr32[0]);
1121 + }
1122 + if (likely(*out)) {
1123 + ebpf_network_viewer_ip_list_t *move = *out;
1124 + while (move) {
1125 + if (in->ver == move->ver &&
1126 + ebpf_is_ip_inside_range(&move->first, &move->last, &in->first, &in->last, in->ver)) {
1127 +#ifdef NETDATA_DEV_MODE
1128 + netdata_log_info(
1129 + "The range/value (%s) is inside the range/value (%s) already inserted, it will be ignored.",
1130 + in->value,
1131 + move->value);
1132 +#endif
1133 + freez(in->value);
1134 + freez(in);
1135 + return;
1136 + }
1137 + move = move->next;
1138 + }
1139 + move = *out;
1140 + while (move->next)
1141 + move = move->next;
1142 + move->next = in;
1143 + } else {
1144 + *out = in;
1145 + }
1146 +
1147 +#ifdef NETDATA_DEV_MODE
1148 + char first[256], last[512];
1149 + if (in->ver == AF_INET) {
1150 + netdata_log_info(
1151 + "Adding values %s: (%u - %u) to %s IP list \"%s\" used on network viewer",
1152 + in->value,
1153 + in->first.addr32[0],
1154 + in->last.addr32[0],
1155 + (*out == network_viewer_opt.included_ips) ? "included" : "excluded",
1156 + table);
1157 + } else {
1158 + if (inet_ntop(AF_INET6, in->first.addr8, first, INET6_ADDRSTRLEN) &&
1159 + inet_ntop(AF_INET6, in->last.addr8, last, INET6_ADDRSTRLEN))
1160 + netdata_log_info(
1161 + "Adding values %s - %s to %s IP list \"%s\" used on network viewer",
1162 + first,
1163 + last,
1164 + (*out == network_viewer_opt.included_ips) ? "included" : "excluded",
1165 + table);
1166 + }
1167 +#endif
1168 +}
1169 +
1170 +/**
1171 + * Parse IP Range
1172 + *
1173 + * Parse the IP ranges given and create Network Viewer IP Structure
1174 + *
1175 + * @param ptr is a pointer with the text to parse.
1176 + */
1177 +void ebpf_parse_ips_unsafe(const char *ptr)
1178 +{
1179 + // No value
1180 + if (unlikely(!ptr))
1181 + return;
1182 +
1183 + while (likely(ptr)) {
1184 + // Move forward until next valid character
1185 + while (isspace(*ptr))
1186 + ptr++;
1187 +
1188 + // No valid value found
1189 + if (unlikely(!*ptr))
1190 + return;
1191 +
1192 + // Find space that ends the list
1193 + char *end = strchr(ptr, ' ');
1194 + if (end) {
1195 + *end++ = '\0';
1196 + }
1197 +
1198 + bool neg = false;
1199 + if (*ptr == '!') {
1200 + neg = true;
1201 + ptr++;
1202 + }
1203 +
1204 + if (isascii(*ptr)) {
1205 + ebpf_parse_ip_list_unsafe(
1206 + neg ? (void **)&network_viewer_opt.excluded_ips : (void **)&network_viewer_opt.included_ips, ptr);
1207 + }
1208 +
1209 + ptr = end;
1210 + }
1211 +}
1212 +/*****************************************************************
1213 + *
1214 + * FUNCTIONS TO CREATE CHARTS
1215 + *
1216 + *****************************************************************/
1217 +
1218 +/**
1219 + * Create apps for module
1220 + *
1221 + * Create apps chart that will be used with specific module
1222 + *
1223 + * @param em the module main structure.
1224 + * @param root a pointer for the targets.
1225 + */
1226 +void ebpf_create_apps_for_module(ebpf_module_t *em, ebpf_target_t *root)
1227 +{
1228 + if (em->enabled < NETDATA_THREAD_EBPF_STOPPING && em->apps_charts && em->functions.apps_routine)
1229 + em->functions.apps_routine(em, root);
1230 +}
1231 +
1232 +/**
1233 + * Create apps charts
1234 + *
1235 + * Call ebpf_create_chart to create the charts on apps submenu.
1236 + *
1237 + * @param root a pointer for the targets.
1238 + */
1239 +void ebpf_create_apps_charts(ebpf_target_t *root)
1240 +{
1241 + // if (unlikely(!ebpf_pids))
1242 + // return;
1243 +
1244 + struct ebpf_target *w;
1245 + int newly_added = 0;
1246 +
1247 + for (w = root; w; w = w->next) {
1248 + if (w->target)
1249 + continue;
1250 +
1251 + if (unlikely(w->processes && (debug_enabled || w->debug_enabled))) {
1252 + struct ebpf_pid_on_target *pid_on_target;
1253 +
1254 + fprintf(
1255 + stderr,
1256 + "ebpf.plugin: target '%s' has aggregated %u process%s:",
1257 + w->name,
1258 + w->processes,
1259 + (w->processes == 1) ? "" : "es");
1260 +
1261 + for (pid_on_target = w->root_pid; pid_on_target; pid_on_target = pid_on_target->next) {
1262 + fprintf(stderr, " %d", pid_on_target->pid);
1263 + }
1264 +
1265 + fputc('\n', stderr);
1266 + }
1267 +
1268 + if (!w->exposed && w->processes) {
1269 + newly_added++;
1270 + w->exposed = 1;
1271 + if (debug_enabled || w->debug_enabled)
1272 + debug_log_int("%s just added - regenerating charts.", w->name);
1273 + }
1274 + }
1275 +
1276 + if (newly_added) {
1277 + int i;
1278 + for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
1279 + if (!(collect_pids & (1 << i)))
1280 + continue;
1281 +
1282 + ebpf_module_t *current = &ebpf_modules[i];
1283 + ebpf_create_apps_for_module(current, root);
1284 + }
1285 + }
1286 +}
1287 +
1288 +/*****************************************************************
1289 + *
1290 + * FUNCTIONS TO READ GLOBAL HASH TABLES
1291 + *
1292 + *****************************************************************/
1293 +
1294 +/**
1295 + * Read Global Table Stats
1296 + *
1297 + * Read data from specified table (map_fd) using array allocated inside thread(values) and storing
1298 + * them in stats vector starting from the first position.
1299 + *
1300 + * For PID tables is recommended to use a function to parse the specific data.
1301 + *
1302 + * @param stats vector used to store data
1303 + * @param values helper to read data from hash tables.
1304 + * @param map_fd table that has data
1305 + * @param maps_per_core Is necessary to read data from all cores?
1306 + * @param begin initial value to query hash table
1307 + * @param end last value that will not be used.
1308 + */
1309 +void ebpf_read_global_table_stats(
1310 + netdata_idx_t *stats,
1311 + netdata_idx_t *values,
1312 + int map_fd,
1313 + int maps_per_core,
1314 + uint32_t begin,
1315 + uint32_t end)
1316 +{
1317 + uint32_t idx;
1318 + int before = (maps_per_core) ? ebpf_nprocs : 1;
1319 +
1320 + for (idx = begin; idx < end; idx++) {
1321 + if (!bpf_map_lookup_elem(map_fd, &idx, values)) {
1322 + netdata_idx_t total = 0;
1323 + int i;
1324 + for (i = 0; i < before; i++)
1325 + total += values[i];
1326 +
1327 + stats[idx - begin] = total;
1328 + }
1329 + }
1330 +}
1331 +
1332 +/**
1333 + * Check if the ip is inside a IP range
1334 + *
1335 + * @param rfirst the first ip address of the range
1336 + * @param rlast the last ip address of the range
1337 + * @param cmpfirst the first ip to compare
1338 + * @param cmplast the last ip to compare
1339 + * @param family the IP family
1340 + *
1341 + * @return It returns 1 if the IP is inside the range and 0 otherwise
1342 + */
1343 +
1344 +static inline void fill_port_list(ebpf_network_viewer_port_list_t **out, ebpf_network_viewer_port_list_t *in)
1345 +{
1346 + if (likely(*out)) {
1347 + ebpf_network_viewer_port_list_t *move = *out;
1348 + uint16_t first = ntohs(in->first);
1349 + uint16_t last = ntohs(in->last);
1350 + while (move) {
1351 + uint16_t cmp_first = ntohs(move->first);
1352 + uint16_t cmp_last = ntohs(move->last);
1353 + if (cmp_first <= first && first <= cmp_last && cmp_first <= last && last <= cmp_last) {
1354 + netdata_log_info(
1355 + "The range/value (%u, %u) is inside the range/value (%u, %u) already inserted, it will be ignored.",
1356 + first,
1357 + last,
1358 + cmp_first,
1359 + cmp_last);
1360 + freez(in->value);
1361 + freez(in);
1362 + return;
1363 + } else if (first <= cmp_first && cmp_first <= last && first <= cmp_last && cmp_last <= last) {
1364 + netdata_log_info(
1365 + "The range (%u, %u) is bigger than previous range (%u, %u) already inserted, the previous will be ignored.",
1366 + first,
1367 + last,
1368 + cmp_first,
1369 + cmp_last);
1370 + freez(move->value);
1371 + move->value = in->value;
1372 + move->first = in->first;
1373 + move->last = in->last;
1374 + freez(in);
1375 + return;
1376 + }
1377 +
1378 + move = move->next;
1379 + }
1380 + move = *out;
1381 + while (move->next)
1382 + move = move->next;
1383 + move->next = in;
1384 + } else {
1385 + *out = in;
1386 + }
1387 +
1388 +#ifdef NETDATA_INTERNAL_CHECKS
1389 + netdata_log_info(
1390 + "Adding values %s( %u, %u) to %s port list used on network viewer",
1391 + in->value,
1392 + in->first,
1393 + in->last,
1394 + (*out == network_viewer_opt.included_port) ? "included" : "excluded");
1395 +#endif
1396 +}
1397 +
1398 +/**
1399 + * Parse Service List
1400 + *
1401 + * @param out a pointer to store the link list
1402 + * @param service the service used to create the structure that will be linked.
1403 + */
1404 +static void ebpf_parse_service_list(void **out, const char *service)
1405 +{
1406 + ebpf_network_viewer_port_list_t **list = (ebpf_network_viewer_port_list_t **)out;
1407 + struct servent *serv = getservbyname((const char *)service, "tcp");
1408 + if (!serv)
1409 + serv = getservbyname((const char *)service, "udp");
1410 +
1411 + if (!serv) {
1412 + netdata_log_info("Cannot resolve the service '%s' with protocols TCP and UDP, it will be ignored", service);
1413 + return;
1414 + }
1415 +
1416 + ebpf_network_viewer_port_list_t *w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
1417 + w->value = strdupz(service);
1418 + w->hash = simple_hash(service);
1419 +
1420 + w->first = w->last = (uint16_t)serv->s_port;
1421 +
1422 + fill_port_list(list, w);
1423 +}
1424 +
1425 +/**
1426 + * Parse port list
1427 + *
1428 + * Parse an allocated port list with the range given
1429 + *
1430 + * @param out a pointer to store the link list
1431 + * @param range the informed range for the user.
1432 + */
1433 +static void ebpf_parse_port_list(void **out, const char *range_param)
1434 +{
1435 + char range[strlen(range_param) + 1];
1436 + strncpyz(range, range_param, strlen(range_param));
1437 +
1438 + int first, last;
1439 + ebpf_network_viewer_port_list_t **list = (ebpf_network_viewer_port_list_t **)out;
1440 +
1441 + char *copied = strdupz(range);
1442 + if (*range == '*' && *(range + 1) == '\0') {
1443 + first = 1;
1444 + last = 65535;
1445 +
1446 + ebpf_clean_port_structure(list);
1447 + goto fillenvpl;
1448 + }
1449 +
1450 + char *end = range;
1451 + //Move while I cannot find a separator
1452 + while (*end && *end != ':' && *end != '-')
1453 + end++;
1454 +
1455 + //It has a range
1456 + if (likely(*end)) {
1457 + *end++ = '\0';
1458 + if (*end == '!') {
1459 + netdata_log_info(
1460 + "The exclusion cannot be in the second part of the range, the range %s will be ignored.", copied);
1461 + freez(copied);
1462 + return;
1463 + }
1464 + last = str2i((const char *)end);
1465 + } else {
1466 + last = 0;
1467 + }
1468 +
1469 + first = str2i((const char *)range);
1470 + if (first < NETDATA_MINIMUM_PORT_VALUE || first > NETDATA_MAXIMUM_PORT_VALUE) {
1471 + netdata_log_info("The first port %d of the range \"%s\" is invalid and it will be ignored!", first, copied);
1472 + freez(copied);
1473 + return;
1474 + }
1475 +
1476 + if (!last)
1477 + last = first;
1478 +
1479 + if (last < NETDATA_MINIMUM_PORT_VALUE || last > NETDATA_MAXIMUM_PORT_VALUE) {
1480 + netdata_log_info(
1481 + "The second port %d of the range \"%s\" is invalid and the whole range will be ignored!", last, copied);
1482 + freez(copied);
1483 + return;
1484 + }
1485 +
1486 + if (first > last) {
1487 + netdata_log_info(
1488 + "The specified order %s is wrong, the smallest value is always the first, it will be ignored!", copied);
1489 + freez(copied);
1490 + return;
1491 + }
1492 +
1493 + ebpf_network_viewer_port_list_t *w;
1494 +fillenvpl:
1495 + w = callocz(1, sizeof(ebpf_network_viewer_port_list_t));
1496 + w->value = copied;
1497 + w->hash = simple_hash(copied);
1498 + w->first = (uint16_t)first;
1499 + w->last = (uint16_t)last;
1500 + w->cmp_first = (uint16_t)first;
1501 + w->cmp_last = (uint16_t)last;
1502 +
1503 + fill_port_list(list, w);
1504 +}
1505 +
1506 +/**
1507 + * Parse Port Range
1508 + *
1509 + * Parse the port ranges given and create Network Viewer Port Structure
1510 + *
1511 + * @param ptr is a pointer with the text to parse.
1512 + */
1513 +void ebpf_parse_ports(const char *ptr)
1514 +{
1515 + // No value
1516 + if (unlikely(!ptr))
1517 + return;
1518 +
1519 + while (likely(ptr)) {
1520 + // Move forward until next valid character
1521 + while (isspace(*ptr))
1522 + ptr++;
1523 +
1524 + // No valid value found
1525 + if (unlikely(!*ptr))
1526 + return;
1527 +
1528 + // Find space that ends the list
1529 + char *end = strchr(ptr, ' ');
1530 + if (end) {
1531 + *end++ = '\0';
1532 + }
1533 +
1534 + bool neg = false;
1535 + if (*ptr == '!') {
1536 + neg = true;
1537 + ptr++;
1538 + }
1539 +
1540 + if (isdigit(*ptr)) { // Parse port
1541 + ebpf_parse_port_list(
1542 + neg ? (void **)&network_viewer_opt.excluded_port : (void **)&network_viewer_opt.included_port, ptr);
1543 + } else if (isalpha(*ptr)) { // Parse service
1544 + ebpf_parse_service_list(
1545 + neg ? (void **)&network_viewer_opt.excluded_port : (void **)&network_viewer_opt.included_port, ptr);
1546 + } else if (*ptr == '*') { // All
1547 + ebpf_parse_port_list(
1548 + neg ? (void **)&network_viewer_opt.excluded_port : (void **)&network_viewer_opt.included_port, ptr);
1549 + }
1550 +
1551 + ptr = end;
1552 + }
1553 +}
1554 +
1555 +/*****************************************************************
1556 + *
1557 + * FUNCTIONS TO DEFINE OPTIONS
1558 + *
1559 + *****************************************************************/
1560 +
1561 +/**
1562 + * Define labels used to generate charts
1563 + *
1564 + * @param is structure with information about number of calls made for a function.
1565 + * @param pio structure used to generate charts.
1566 + * @param dim a pointer for the dimensions name
1567 + * @param name a pointer for the tensor with the name of the functions.
1568 + * @param algorithm a vector with the algorithms used to make the charts
1569 + * @param end the number of elements in the previous 4 arguments.
1570 + */
1571 +void ebpf_global_labels(
1572 + netdata_syscall_stat_t *is,
1573 + netdata_publish_syscall_t *pio,
1574 + char **dim,
1575 + char **name,
1576 + int *algorithm,
1577 + int end)
1578 +{
1579 + int i;
1580 +
1581 + netdata_syscall_stat_t *prev = NULL;
1582 + netdata_publish_syscall_t *publish_prev = NULL;
1583 + for (i = 0; i < end; i++) {
1584 + if (prev) {
1585 + prev->next = &is[i];
1586 + }
1587 + prev = &is[i];
1588 +
1589 + pio[i].dimension = dim[i];
1590 + pio[i].name = name[i];
1591 + pio[i].algorithm = ebpf_algorithms[algorithm[i]];
1592 + if (publish_prev) {
1593 + publish_prev->next = &pio[i];
1594 + }
1595 + publish_prev = &pio[i];
1596 + }
1597 +}
1598 +
1599 +/**
1600 + * Disable all Global charts
1601 + *
1602 + * Disable charts
1603 + */
1604 +void disable_all_global_charts()
1605 +{
1606 + int i;
1607 + for (i = 0; ebpf_modules[i].info.thread_name; i++) {
1608 + ebpf_modules[i].enabled = NETDATA_THREAD_EBPF_NOT_RUNNING;
1609 + ebpf_modules[i].global_charts = 0;
1610 + }
1611 +}
1612 +
1613 +/**
1614 + * Disable Cgroups
1615 + *
1616 + * Disable charts for apps loading only global charts.
1617 + */
1618 +void ebpf_disable_cgroups()
1619 +{
1620 + int i;
1621 + for (i = 0; ebpf_modules[i].info.thread_name; i++) {
1622 + ebpf_modules[i].cgroup_charts = 0;
1623 + }
1624 +}
1625 +
1626 +/**
1627 + * Update Disabled Plugins
1628 + *
1629 + * This function calls ebpf_update_stats to update statistics for collector.
1630 + *
1631 + * @param em a pointer to `struct ebpf_module`
1632 + */
1633 +void ebpf_update_disabled_plugin_stats(ebpf_module_t *em)
1634 +{
1635 + netdata_mutex_lock(&lock);
1636 + ebpf_update_stats(&plugin_statistics, em);
1637 + netdata_mutex_unlock(&lock);
1638 +}
1639 +
1640 +/**
1641 + * Print help on standard error for user knows how to use the collector.
1642 + */
1643 +void ebpf_print_help()
1644 +{
1645 + fprintf(
1646 + stderr,
1647 + "\n"
1648 + " Netdata ebpf.plugin %s\n"
1649 + " Copyright 2018-2025 Netdata Inc.\n"
1650 + " Released under GNU General Public License v3 or later.\n"
1651 + "\n"
1652 + " This eBPF.plugin is a data collector plugin for netdata.\n"
1653 + "\n"
1654 + " This plugin only accepts long options with one or two dashes. The available command line options are:\n"
1655 + "\n"
1656 + " SECONDS Set the data collection frequency.\n"
1657 + "\n"
1658 + " [-]-help Show this help.\n"
1659 + "\n"
1660 + " [-]-version Show software version.\n"
1661 + "\n"
1662 + " [-]-global Disable charts per application and cgroup.\n"
1663 + "\n"
1664 + " [-]-all Enable all chart groups (global, apps, and cgroup), unless -g is also given.\n"
1665 + "\n"
1666 + " [-]-cachestat Enable charts related to process run time.\n"
1667 + "\n"
1668 + " [-]-dcstat Enable charts related to directory cache.\n"
1669 + "\n"
1670 + " [-]-disk Enable charts related to disk monitoring.\n"
1671 + "\n"
1672 + " [-]-filesystem Enable chart related to filesystem run time.\n"
1673 + "\n"
1674 + " [-]-hardirq Enable chart related to hard IRQ latency.\n"
1675 + "\n"
1676 + " [-]-mdflush Enable charts related to multi-device flush.\n"
1677 + "\n"
1678 + " [-]-mount Enable charts related to mount monitoring.\n"
1679 + "\n"
1680 + " [-]-net Enable network viewer charts.\n"
1681 + "\n"
1682 + " [-]-oomkill Enable chart related to OOM kill tracking.\n"
1683 + "\n"
1684 + " [-]-process Enable charts related to process run time.\n"
1685 + "\n"
1686 + " [-]-return Run the collector in return mode.\n"
1687 + "\n"
1688 + " [-]-shm Enable chart related to shared memory tracking.\n"
1689 + "\n"
1690 + " [-]-softirq Enable chart related to soft IRQ latency.\n"
1691 + "\n"
1692 + " [-]-sync Enable chart related to sync run time.\n"
1693 + "\n"
1694 + " [-]-swap Enable chart related to swap run time.\n"
1695 + "\n"
1696 + " [-]-vfs Enable chart related to vfs run time.\n"
1697 + "\n"
1698 + " [-]-legacy Load legacy eBPF programs.\n"
1699 + "\n"
1700 + " [-]-core Use CO-RE when available(Working in progress).\n"
1701 + "\n",
1702 + NETDATA_VERSION);
1703 +}
1704 +
1705 +/*****************************************************************
1706 + *
1707 + * TRACEPOINT MANAGEMENT FUNCTIONS
1708 + *
1709 + *****************************************************************/
1710 +
1711 +/**
1712 + * Enable a tracepoint.
1713 + *
1714 + * @return 0 on success, -1 on error.
1715 + */
1716 +int ebpf_enable_tracepoint(ebpf_tracepoint_t *tp)
1717 +{
1718 + int test = ebpf_is_tracepoint_enabled(tp->class, tp->event);
1719 +
1720 + // err?
1721 + if (test == -1) {
1722 + return -1;
1723 + }
1724 + // disabled?
1725 + else if (test == 0) {
1726 + // enable it then.
1727 + if (ebpf_enable_tracing_values(tp->class, tp->event)) {
1728 + return -1;
1729 + }
1730 + }
1731 +
1732 + // enabled now or already was.
1733 + tp->enabled = true;
1734 +
1735 + return 0;
1736 +}
1737 +
1738 +/**
1739 + * Disable a tracepoint if it's enabled.
1740 + *
1741 + * @return 0 on success, -1 on error.
1742 + */
1743 +int ebpf_disable_tracepoint(ebpf_tracepoint_t *tp)
1744 +{
1745 + int test = ebpf_is_tracepoint_enabled(tp->class, tp->event);
1746 +
1747 + // err?
1748 + if (test == -1) {
1749 + return -1;
1750 + }
1751 + // enabled?
1752 + else if (test == 1) {
1753 + // disable it then.
1754 + if (ebpf_disable_tracing_values(tp->class, tp->event)) {
1755 + return -1;
1756 + }
1757 + }
1758 +
1759 + // disable now or already was.
1760 + tp->enabled = false;
1761 +
1762 + return 0;
1763 +}
1764 +
1765 +/**
1766 + * Enable multiple tracepoints on a list of tracepoints which end when the
1767 + * class is NULL.
1768 + *
1769 + * @return the number of successful enables.
1770 + */
1771 +uint32_t ebpf_enable_tracepoints(ebpf_tracepoint_t *tps)
1772 +{
1773 + uint32_t cnt = 0;
1774 + for (int i = 0; tps[i].class != NULL; i++) {
1775 + if (ebpf_enable_tracepoint(&tps[i]) == -1) {
1776 + netdata_log_error("Failed to enable tracepoint %s:%s", tps[i].class, tps[i].event);
1777 + } else {
1778 + cnt++;
1779 + }
1780 + }
1781 + return cnt;
1782 +}
1783 +
1784 +/*****************************************************************
1785 + *
1786 + * AUXILIARY FUNCTIONS USED DURING INITIALIZATION
1787 + *
1788 + *****************************************************************/
1789 +
1790 +/**
1791 + * Read Local Ports
1792 + *
1793 + * Parse /proc/net/{tcp,udp} and get the ports Linux is listening.
1794 + *
1795 + * @param filename the proc file to parse.
1796 + * @param proto is the magic number associated to the protocol file we are reading.
1797 + */
1798 +void read_local_ports(char *filename, uint8_t proto)
1799 +{
1800 + procfile *ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
1801 + if (!ff)
1802 + return;
1803 +
1804 + ff = procfile_readall(ff);
1805 + if (!ff)
1806 + return;
1807 +
1808 + size_t lines = procfile_lines(ff), l;
1809 + netdata_passive_connection_t values = {.counter = 0, .tgid = 0, .pid = 0};
1810 + for (l = 0; l < lines; l++) {
1811 + size_t words = procfile_linewords(ff, l);
1812 + // This is header or end of file
1813 + if (unlikely(words < 14))
1814 + continue;
1815 +
1816 + // https://elixir.bootlin.com/linux/v5.7.8/source/include/net/tcp_states.h
1817 + // 0A = TCP_LISTEN
1818 + if (strcmp("0A", procfile_lineword(ff, l, 5)))
1819 + continue;
1820 +
1821 + // Read local port
1822 + uint16_t port = (uint16_t)strtol(procfile_lineword(ff, l, 2), NULL, 16);
1823 + update_listen_table(htons(port), proto, &values);
1824 + }
1825 +
1826 + procfile_close(ff);
1827 +}
1828 +
1829 +/**
1830 + * Read Local addresseses
1831 + *
1832 + * Read the local address from the interfaces.
1833 + */
1834 +void ebpf_read_local_addresses_unsafe()
1835 +{
1836 + struct ifaddrs *ifaddr, *ifa;
1837 + if (getifaddrs(&ifaddr) == -1) {
1838 + netdata_log_error(
1839 + "Cannot get the local IP addresses, it is no possible to do separation between inbound and outbound connections");
1840 + return;
1841 + }
1842 +
1843 + char *notext = {"No text representation"};
1844 + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
1845 + if (ifa->ifa_addr == NULL)
1846 + continue;
1847 +
1848 + if ((ifa->ifa_addr->sa_family != AF_INET) && (ifa->ifa_addr->sa_family != AF_INET6))
1849 + continue;
1850 +
1851 + ebpf_network_viewer_ip_list_t *w = callocz(1, sizeof(ebpf_network_viewer_ip_list_t));
1852 +
1853 + int family = ifa->ifa_addr->sa_family;
1854 + w->ver = (uint8_t)family;
1855 + char text[INET6_ADDRSTRLEN];
1856 + if (family == AF_INET) {
1857 + struct sockaddr_in *in = (struct sockaddr_in *)ifa->ifa_addr;
1858 +
1859 + w->first.addr32[0] = in->sin_addr.s_addr;
1860 + w->last.addr32[0] = in->sin_addr.s_addr;
1861 +
1862 + if (inet_ntop(AF_INET, w->first.addr8, text, INET6_ADDRSTRLEN)) {
1863 + w->value = strdupz(text);
1864 + w->hash = simple_hash(text);
1865 + } else {
1866 + w->value = strdupz(notext);
1867 + w->hash = simple_hash(notext);
1868 + }
1869 + } else {
1870 + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)ifa->ifa_addr;
1871 +
1872 + memcpy(w->first.addr8, (void *)&in6->sin6_addr, sizeof(struct in6_addr));
1873 + memcpy(w->last.addr8, (void *)&in6->sin6_addr, sizeof(struct in6_addr));
1874 +
1875 + if (inet_ntop(AF_INET6, w->first.addr8, text, INET6_ADDRSTRLEN)) {
1876 + w->value = strdupz(text);
1877 + w->hash = simple_hash(text);
1878 + } else {
1879 + w->value = strdupz(notext);
1880 + w->hash = simple_hash(notext);
1881 + }
1882 + }
1883 +
1884 + ebpf_fill_ip_list_unsafe(
1885 + (family == AF_INET) ? &network_viewer_opt.ipv4_local_ip : &network_viewer_opt.ipv6_local_ip, w, "selector");
1886 + }
1887 +
1888 + freeifaddrs(ifaddr);
1889 +}
src/collectors/ebpf.plugin/libbpf_api/ebpf_library.h new
+183
@@ -0,0 +1,183 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_COLLECTOR_EBPF_LIBRARY_H
4 +#define NETDATA_COLLECTOR_EBPF_LIBRARY_H 1
5 +
6 +#include <stdint.h>
7 +#include "../ebpf_socket_ipc.h"
8 +
9 +typedef struct netdata_publish_syscall netdata_publish_syscall_t;
10 +typedef struct netdata_syscall_stat netdata_syscall_stat_t;
11 +typedef struct ebpf_module ebpf_module_t;
12 +typedef struct ebpf_target ebpf_target_t;
13 +typedef struct ebpf_tracepoint ebpf_tracepoint_t;
14 +typedef struct aral ARAL;
15 +typedef struct config config;
16 +
17 +/*****************************************************************
18 + *
19 + * DIMENSION WRITING FUNCTIONS
20 + *
21 + *****************************************************************/
22 +
23 +void write_chart_dimension(const char *dim, long long value);
24 +void ebpf_write_global_dimension(char *name, char *id, char *algorithm);
25 +void ebpf_create_global_dimension(void *ptr, int end);
26 +
27 +/*****************************************************************
28 + *
29 + * CHART WRITING FUNCTIONS
30 + *
31 + *****************************************************************/
32 +
33 +void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, uint32_t end);
34 +void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end);
35 +void ebpf_one_dimension_write_charts(char *family, char *chart, char *dim, long long v1);
36 +void write_io_chart(char *chart, char *family, char *dwrite, long long vwrite, char *dread, long long vread);
37 +void write_histogram_chart(char *family, char *name, const uint64_t *hist, char **dimensions, uint32_t end);
38 +
39 +/*****************************************************************
40 + *
41 + * CHART CREATION FUNCTIONS
42 + *
43 + *****************************************************************/
44 +
45 +void ebpf_write_chart_cmd(
46 + char *type,
47 + char *id,
48 + char *suffix,
49 + char *title,
50 + char *units,
51 + char *family,
52 + char *charttype,
53 + char *context,
54 + int order,
55 + int update_every,
56 + char *module);
57 +
58 +void ebpf_write_chart_obsolete(
59 + char *type,
60 + const char *id,
61 + char *suffix,
62 + char *title,
63 + char *units,
64 + char *family,
65 + char *charttype,
66 + const char *context,
67 + int order,
68 + int update_every);
69 +
70 +void ebpf_create_chart(
71 + char *type,
72 + char *id,
73 + char *title,
74 + char *units,
75 + char *family,
76 + char *context,
77 + char *charttype,
78 + int order,
79 + void (*ncd)(void *, int),
80 + void *move,
81 + int end,
82 + int update_every,
83 + char *module);
84 +
85 +/*****************************************************************
86 + *
87 + * ARAL STATISTIC CHARTS
88 + *
89 + *****************************************************************/
90 +
91 +int ebpf_statistic_create_aral_chart(char *name, ebpf_module_t *em);
92 +void ebpf_statistic_obsolete_aral_chart(ebpf_module_t *em, int prio);
93 +void ebpf_send_data_aral_chart(ARAL *memory, ebpf_module_t *em);
94 +
95 +/*****************************************************************
96 + *
97 + * CONFIG FILE PARSER FUNCTIONS
98 + *
99 + *****************************************************************/
100 +
101 +void ebpf_how_to_load(const char *ptr);
102 +void ebpf_set_apps_mode(netdata_apps_integration_flags_t value);
103 +void ebpf_update_interval(int update_every);
104 +void ebpf_update_table_size();
105 +void ebpf_update_lifetime();
106 +void ebpf_set_load_mode(netdata_ebpf_load_mode_t load, netdata_ebpf_load_mode_t origin);
107 +void ebpf_update_load_mode(const char *str, netdata_ebpf_load_mode_t origin);
108 +void ebpf_update_map_per_core();
109 +void ebpf_set_ipc_value(const char *integration);
110 +void ebpf_parse_ipc_section();
111 +void ebpf_set_thread_mode(netdata_run_mode_t lmode);
112 +void ebpf_enable_chart(int idx, int disable_cgroup);
113 +void ebpf_enable_specific_chart(ebpf_module_t *em, int disable_cgroup);
114 +void read_collector_values(int *disable_cgroups, int update_every, netdata_ebpf_load_mode_t origin);
115 +void parse_network_viewer_section(struct config *cfg);
116 +void ebpf_parse_service_name_section(struct config *cfg);
117 +void ebpf_parse_ports(const char *ptr);
118 +void ebpf_parse_ips_unsafe(const char *ptr);
119 +void ebpf_read_local_addresses_unsafe();
120 +int ebpf_load_collector_config(char *path, int *disable_cgroups, int update_every);
121 +void ebpf_load_thread_config();
122 +
123 +/*****************************************************************
124 + *
125 + * FUNCTIONS TO CREATE CHARTS
126 + *
127 + *****************************************************************/
128 +
129 +void ebpf_create_apps_for_module(ebpf_module_t *em, ebpf_target_t *root);
130 +void ebpf_create_apps_charts(ebpf_target_t *root);
131 +
132 +/*****************************************************************
133 + *
134 + * FUNCTIONS TO READ GLOBAL HASH TABLES
135 + *
136 + *****************************************************************/
137 +
138 +void ebpf_read_global_table_stats(
139 + netdata_idx_t *stats,
140 + netdata_idx_t *values,
141 + int map_fd,
142 + int maps_per_core,
143 + uint32_t begin,
144 + uint32_t end);
145 +
146 +/*****************************************************************
147 + *
148 + * FUNCTIONS TO DEFINE OPTIONS
149 + *
150 + *****************************************************************/
151 +
152 +void ebpf_global_labels(
153 + netdata_syscall_stat_t *is,
154 + netdata_publish_syscall_t *pio,
155 + char **dim,
156 + char **name,
157 + int *algorithm,
158 + int end);
159 +
160 +void disable_all_global_charts();
161 +void ebpf_disable_cgroups();
162 +void ebpf_update_disabled_plugin_stats(ebpf_module_t *em);
163 +void ebpf_print_help();
164 +
165 +/*****************************************************************
166 + *
167 + * TRACEPOINT MANAGEMENT FUNCTIONS
168 + *
169 + *****************************************************************/
170 +
171 +int ebpf_enable_tracepoint(ebpf_tracepoint_t *tp);
172 +int ebpf_disable_tracepoint(ebpf_tracepoint_t *tp);
173 +uint32_t ebpf_enable_tracepoints(ebpf_tracepoint_t *tps);
174 +
175 +/*****************************************************************
176 + *
177 + * AUXILIARY FUNCTIONS USED DURING INITIALIZATION
178 + *
179 + *****************************************************************/
180 +
181 +void read_local_ports(char *filename, uint8_t proto);
182 +
183 +#endif /* NETDATA_COLLECTOR_EBPF_LIBRARY_H */
src/collectors/ebpf.plugin/metadata.yaml
+16 -16
@@ -74,7 +74,7 @@ modules:
74 list:
75 - name: update every
76 description: Data collection frequency.
77 - default_value: 5
77 + default_value: 10
78 required: false
79 - name: ebpf load mode
80 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -300,7 +300,7 @@ modules:
300 list:
301 - name: update every
302 description: Data collection frequency.
303 - default_value: 5
303 + default_value: 10
304 required: false
305 - name: ebpf load mode
306 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -550,7 +550,7 @@ modules:
550 list:
551 - name: update every
552 description: Data collection frequency.
553 - default_value: 5
553 + default_value: 10
554 required: false
555 - name: ebpf load mode
556 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -655,7 +655,7 @@ modules:
655 list:
656 - name: update every
657 description: Data collection frequency.
658 - default_value: 5
658 + default_value: 10
659 required: false
660 - name: ebpf load mode
661 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -763,7 +763,7 @@ modules:
763 list:
764 - name: update every
765 description: Data collection frequency.
766 - default_value: 5
766 + default_value: 10
767 required: false
768 - name: ebpf load mode
769 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -994,7 +994,7 @@ modules:
994 list:
995 - name: update every
996 description: Data collection frequency.
997 - default_value: 5
997 + default_value: 10
998 required: false
999 - name: ebpf load mode
1000 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -1171,7 +1171,7 @@ modules:
1171 list:
1172 - name: update every
1173 description: Data collection frequency.
1174 - default_value: 5
1174 + default_value: 10
1175 required: false
1176 - name: ebpf load mode
1177 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -1280,7 +1280,7 @@ modules:
1280 list:
1281 - name: update every
1282 description: Data collection frequency.
1283 - default_value: 5
1283 + default_value: 10
1284 required: false
1285 - name: ebpf load mode
1286 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -1469,7 +1469,7 @@ modules:
1469 list:
1470 - name: update every
1471 description: Data collection frequency.
1472 - default_value: 5
1472 + default_value: 10
1473 required: false
1474 - name: ebpf load mode
1475 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -1590,7 +1590,7 @@ modules:
1590 list:
1591 - name: update every
1592 description: Data collection frequency.
1593 - default_value: 5
1593 + default_value: 10
1594 required: false
1595 - name: ebpf load mode
1596 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -1946,7 +1946,7 @@ modules:
1946 list:
1947 - name: update every
1948 description: Data collection frequency.
1949 - default_value: 5
1949 + default_value: 10
1950 required: false
1951 - name: ebpf load mode
1952 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -2169,7 +2169,7 @@ modules:
2169 list:
2170 - name: update every
2171 description: Data collection frequency.
2172 - default_value: 5
2172 + default_value: 10
2173 required: false
2174 - name: ebpf load mode
2175 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -2332,7 +2332,7 @@ modules:
2332 list:
2333 - name: update every
2334 description: Data collection frequency.
2335 - default_value: 5
2335 + default_value: 10
2336 required: false
2337 - name: ebpf load mode
2338 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -2562,7 +2562,7 @@ modules:
2562 list:
2563 - name: update every
2564 description: Data collection frequency.
2565 - default_value: 5
2565 + default_value: 10
2566 required: false
2567 - name: ebpf load mode
2568 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -2669,7 +2669,7 @@ modules:
2669 list:
2670 - name: update every
2671 description: Data collection frequency.
2672 - default_value: 5
2672 + default_value: 10
2673 required: false
2674 - name: ebpf load mode
2675 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).
@@ -2795,7 +2795,7 @@ modules:
2795 list:
2796 - name: update every
2797 description: Data collection frequency.
2798 - default_value: 5
2798 + default_value: 10
2799 required: false
2800 - name: ebpf load mode
2801 description: Define whether plugin will monitor the call (`entry`) for the functions or it will also monitor the return (`return`).