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, ¤t, &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`).