@cryptotaxi247 / netdata-1 / commits / e8e60465b

Ebpf swap (#11090)

Add new thread to ebpf.plugin.

thiagoftsm committed May 24, 2021 at 12:53 UTC e8e60465bf8b659a7acb821abb26541fa9809e05
14 files changed +556 -7
CMakeLists.txt
+2
@@ -496,6 +496,8 @@ set(EBPF_PROCESS_PLUGIN_FILES
496 collectors/ebpf.plugin/ebpf_socket.h
497 collectors/ebpf.plugin/ebpf_sync.c
498 collectors/ebpf.plugin/ebpf_sync.h
499 + collectors/ebpf.plugin/ebpf_swap.c
500 + collectors/ebpf.plugin/ebpf_swap.h
501 collectors/ebpf.plugin/ebpf_apps.c
502 collectors/ebpf.plugin/ebpf_apps.h
503 )
Makefile.am
+2
@@ -295,6 +295,8 @@ EBPF_PLUGIN_FILES = \
295 collectors/ebpf.plugin/ebpf_socket.h \
296 collectors/ebpf.plugin/ebpf_sync.c \
297 collectors/ebpf.plugin/ebpf_sync.h \
298 + collectors/ebpf.plugin/ebpf_swap.c \
299 + collectors/ebpf.plugin/ebpf_swap.h \
300 collectors/ebpf.plugin/ebpf.h \
301 collectors/ebpf.plugin/ebpf_apps.c \
302 collectors/ebpf.plugin/ebpf_apps.h \
collectors/ebpf.plugin/Makefile.am
+1
@@ -37,4 +37,5 @@ dist_ebpfconfig_DATA = \
37 ebpf.d/network.conf \
38 ebpf.d/process.conf \
39 ebpf.d/sync.conf \
40 + ebpf.d/swap.conf \
41 $(NULL)
collectors/ebpf.plugin/ebpf.c
+32 -2
@@ -93,7 +93,11 @@ ebpf_module_t ebpf_modules[] = {
93 { .thread_name = "dc", .config_name = "dc", .enabled = 0, .start_routine = ebpf_dcstat_thread,
94 .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY,
95 .optional = 0, .apps_routine = ebpf_dcstat_create_apps_charts, .maps = NULL,
96 - .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE },
96 + .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL },
97 + { .thread_name = "swap", .config_name = "swap", .enabled = 0, .start_routine = ebpf_swap_thread,
98 + .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY,
99 + .optional = 0, .apps_routine = ebpf_swap_create_apps_charts, .maps = NULL,
100 + .pid_map_size = ND_EBPF_DEFAULT_PID_SIZE, .names = NULL },
101 { .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_time = 1,
102 .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY,
103 .optional = 0, .apps_routine = NULL, .maps = NULL, .pid_map_size = 0, .names = NULL },
@@ -155,6 +159,12 @@ static void ebpf_exit(int sig)
159 freez(dcstat_pid);
160 }
161
162 + if (ebpf_modules[EBPF_MODULE_SWAP_IDX].enabled) {
163 + ebpf_modules[EBPF_MODULE_SWAP_IDX].enabled = 0;
164 + clean_swap_pid_structures();
165 + freez(swap_pid);
166 + }
167 +
168 /*
169 int ret = fork();
170 if (ret < 0) // error
@@ -605,6 +615,8 @@ void ebpf_print_help()
615 "\n",
616 " --sync or -s Enable chart related to sync run time.\n"
617 "\n"
618 + " --swap or -w Enable chart related to swap run time.\n"
619 + "\n"
620 VERSION,
621 (year >= 116) ? year + 1900 : 2020);
622 }
@@ -894,6 +906,13 @@ static void read_collector_values(int *disable_apps)
906 started++;
907 }
908
909 + enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION, "swap",
910 + CONFIG_BOOLEAN_NO);
911 + if (enabled) {
912 + ebpf_enable_chart(EBPF_MODULE_SWAP_IDX, *disable_apps);
913 + started++;
914 + }
915 +
916 if (!started){
917 ebpf_enable_all_charts(*disable_apps);
918 // Read network viewer section
@@ -982,6 +1001,7 @@ static void parse_args(int argc, char **argv)
1001 {"process", no_argument, 0, 'p' },
1002 {"return", no_argument, 0, 'r' },
1003 {"sync", no_argument, 0, 's' },
1004 + {"swap", no_argument, 0, 'w' },
1005 {0, 0, 0, 0}
1006 };
1007
@@ -996,7 +1016,7 @@ static void parse_args(int argc, char **argv)
1016 }
1017
1018 while (1) {
999 - int c = getopt_long(argc, argv, "hvgacdnprs", long_options, &option_index);
1019 + int c = getopt_long(argc, argv, "hvgacdnprsw", long_options, &option_index);
1020 if (c == -1)
1021 break;
1022
@@ -1072,6 +1092,14 @@ static void parse_args(int argc, char **argv)
1092 ebpf_enable_chart(EBPF_MODULE_SYNC_IDX, disable_apps);
1093 #ifdef NETDATA_INTERNAL_CHECKS
1094 info("EBPF enabling \"sync\" chart, because it was started with the option \"--sync\" or \"-s\".");
1095 +#endif
1096 + break;
1097 + }
1098 + case 'w': {
1099 + enabled = 1;
1100 + ebpf_enable_chart(EBPF_MODULE_SWAP_IDX, disable_apps);
1101 +#ifdef NETDATA_INTERNAL_CHECKS
1102 + info("EBPF enabling \"swap\" chart, because it was started with the option \"--swap\" or \"-w\".");
1103 #endif
1104 break;
1105 }
@@ -1203,6 +1231,8 @@ int main(int argc, char **argv)
1231 NULL, NULL, ebpf_modules[EBPF_MODULE_SYNC_IDX].start_routine},
1232 {"EBPF DCSTAT" , NULL, NULL, 1,
1233 NULL, NULL, ebpf_modules[EBPF_MODULE_DCSTAT_IDX].start_routine},
1234 + {"EBPF SWAP" , NULL, NULL, 1,
1235 + NULL, NULL, ebpf_modules[EBPF_MODULE_SWAP_IDX].start_routine},
1236 {NULL , NULL, NULL, 0,
1237 NULL, NULL, NULL}
1238 };
collectors/ebpf.plugin/ebpf.d.conf
+2
@@ -31,11 +31,13 @@
31 # `socket` : This eBPF program creates charts with information about `TCP` and `UDP` functions, including the
32 # bandwidth consumed by each.
33 # `sync` : Montitor calls for syscall sync(2).
34 +# `swap` : Monitor calls for internal swap functions.
35 [ebpf programs]
36 cachestat = no
37 dcstat = no
38 process = yes
39 socket = yes
40 sync = yes
41 + swap = no
42 network connections = no
43
collectors/ebpf.plugin/ebpf.d/swap.conf new
+15
@@ -0,0 +1,15 @@
1 +# The `ebpf load mode` option accepts the following values :
2 +# `entry` : The eBPF collector only monitors calls for the functions, and does not show charts related to errors.
3 +# `return : In the `return` mode, the eBPF collector monitors the same kernel functions as `entry`, but also creates
4 +# new charts for the return of these functions, such as errors.
5 +#
6 +# The eBPF collector also creates charts for each running application through an integration with the `apps plugin`.
7 +# If you want to disable the integration with `apps.plugin` along with the above charts, change the setting `apps` to
8 +# 'no'.
9 +#
10 +#
11 +[global]
12 + ebpf load mode = entry
13 + apps = yes
14 + update every = 2
15 + pid table size = 32768
collectors/ebpf.plugin/ebpf.h
+4 -1
@@ -78,7 +78,8 @@ enum ebpf_module_indexes {
78 EBPF_MODULE_SOCKET_IDX,
79 EBPF_MODULE_CACHESTAT_IDX,
80 EBPF_MODULE_SYNC_IDX,
81 - EBPF_MODULE_DCSTAT_IDX
81 + EBPF_MODULE_DCSTAT_IDX,
82 + EBPF_MODULE_SWAP_IDX
83 };
84
85 // Copied from musl header
@@ -96,6 +97,8 @@ enum ebpf_module_indexes {
97 #define NETDATA_EBPF_CHART_TYPE_LINE "line"
98 #define NETDATA_EBPF_CHART_TYPE_STACKED "stacked"
99 #define NETDATA_EBPF_MEMORY_GROUP "mem"
100 +#define NETDATA_EBPF_SYSTEM_GROUP "system"
101 +#define NETDATA_SYSTEM_SWAP_SUBMENU "swap"
102
103 // Log file
104 #define NETDATA_DEVELOPER_LOG_FILE "developer.log"
collectors/ebpf.plugin/ebpf_apps.c
+6
@@ -933,6 +933,12 @@ void cleanup_variables_from_other_threads(uint32_t pid)
933 freez(dcstat_pid[pid]);
934 dcstat_pid[pid] = NULL;
935 }
936 +
937 + // Clean swap structure
938 + if (swap_pid) {
939 + freez(swap_pid[pid]);
940 + swap_pid[pid] = NULL;
941 + }
942 }
943
944 /**
collectors/ebpf.plugin/ebpf_apps.h
+2
@@ -22,6 +22,7 @@
22 #include "ebpf_dcstat.h"
23 #include "ebpf_cachestat.h"
24 #include "ebpf_sync.h"
25 +#include "ebpf_swap.h"
26
27 #define MAX_COMPARE_NAME 100
28 #define MAX_NAME 100
@@ -113,6 +114,7 @@ struct target {
114 // Changes made to simplify integration between apps and eBPF.
115 netdata_publish_cachestat_t cachestat;
116 netdata_publish_dcstat_t dcstat;
117 + netdata_publish_swap_t swap;
118
119 /* These variables are not necessary for eBPF collector
120 kernel_uint_t minflt;
collectors/ebpf.plugin/ebpf_swap.c new
+437
@@ -0,0 +1,437 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "ebpf.h"
4 +#include "ebpf_swap.h"
5 +
6 +static char *swap_dimension_name[NETDATA_SWAP_END] = { "read", "write" };
7 +static netdata_syscall_stat_t swap_aggregated_data[NETDATA_SWAP_END];
8 +static netdata_publish_syscall_t swap_publish_aggregated[NETDATA_SWAP_END];
9 +
10 +static int read_thread_closed = 1;
11 +static int *map_fd = NULL;
12 +netdata_publish_swap_t *swap_vector = NULL;
13 +
14 +static netdata_idx_t swap_hash_values[NETDATA_SWAP_END];
15 +
16 +netdata_publish_swap_t **swap_pid = NULL;
17 +
18 +static ebpf_data_t swap_data;
19 +struct config swap_config = { .first_section = NULL,
20 + .last_section = NULL,
21 + .mutex = NETDATA_MUTEX_INITIALIZER,
22 + .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
23 + .rwlock = AVL_LOCK_INITIALIZER } };
24 +
25 +static ebpf_local_maps_t swap_maps[] = {{.name = "tbl_pid_swap", .internal_input = ND_EBPF_DEFAULT_PID_SIZE,
26 + .user_input = 0},
27 + {.name = NULL, .internal_input = 0, .user_input = 0}};
28 +
29 +static struct bpf_link **probe_links = NULL;
30 +static struct bpf_object *objects = NULL;
31 +
32 +struct netdata_static_thread swap_threads = {"SWAP KERNEL", NULL, NULL, 1,
33 + NULL, NULL, NULL};
34 +
35 +/*****************************************************************
36 + *
37 + * FUNCTIONS TO CLOSE THE THREAD
38 + *
39 + *****************************************************************/
40 +
41 +/**
42 + * Clean swap structure
43 + */
44 +void clean_swap_pid_structures() {
45 + struct pid_stat *pids = root_of_pids;
46 + while (pids) {
47 + freez(swap_pid[pids->pid]);
48 +
49 + pids = pids->next;
50 + }
51 +}
52 +
53 +/**
54 + * Clean up the main thread.
55 + *
56 + * @param ptr thread data.
57 + */
58 +static void ebpf_swap_cleanup(void *ptr)
59 +{
60 + ebpf_module_t *em = (ebpf_module_t *)ptr;
61 + if (!em->enabled)
62 + return;
63 +
64 + heartbeat_t hb;
65 + heartbeat_init(&hb);
66 + uint32_t tick = 2 * USEC_PER_MS;
67 + while (!read_thread_closed) {
68 + usec_t dt = heartbeat_next(&hb, tick);
69 + UNUSED(dt);
70 + }
71 +
72 + ebpf_cleanup_publish_syscall(swap_publish_aggregated);
73 +
74 + freez(swap_vector);
75 +
76 + struct bpf_program *prog;
77 + size_t i = 0 ;
78 + bpf_object__for_each_program(prog, objects) {
79 + bpf_link__destroy(probe_links[i]);
80 + i++;
81 + }
82 + bpf_object__close(objects);
83 +}
84 +
85 +/*****************************************************************
86 + *
87 + * COLLECTOR THREAD
88 + *
89 + *****************************************************************/
90 +
91 +/**
92 + * Apps Accumulator
93 + *
94 + * Sum all values read from kernel and store in the first address.
95 + *
96 + * @param out the vector with read values.
97 + */
98 +static void swap_apps_accumulator(netdata_publish_swap_t *out)
99 +{
100 + int i, end = (running_on_kernel >= NETDATA_KERNEL_V4_15) ? ebpf_nprocs : 1;
101 + netdata_publish_swap_t *total = &out[0];
102 + for (i = 1; i < end; i++) {
103 + netdata_publish_swap_t *w = &out[i];
104 + total->write += w->write;
105 + total->read += w->read;
106 + }
107 +}
108 +
109 +/**
110 + * Fill PID
111 + *
112 + * Fill PID structures
113 + *
114 + * @param current_pid pid that we are collecting data
115 + * @param out values read from hash tables;
116 + */
117 +static void swap_fill_pid(uint32_t current_pid, netdata_publish_swap_t *publish)
118 +{
119 + netdata_publish_swap_t *curr = swap_pid[current_pid];
120 + if (!curr) {
121 + curr = callocz(1, sizeof(netdata_publish_swap_t));
122 + swap_pid[current_pid] = curr;
123 + }
124 +
125 + memcpy(curr, publish, sizeof(netdata_publish_swap_t));
126 +}
127 +
128 +/**
129 + * Read APPS table
130 + *
131 + * Read the apps table and store data inside the structure.
132 + */
133 +static void read_apps_table()
134 +{
135 + netdata_publish_swap_t *cv = swap_vector;
136 + uint32_t key;
137 + struct pid_stat *pids = root_of_pids;
138 + int fd = map_fd[NETDATA_PID_SWAP_TABLE];
139 + size_t length = sizeof(netdata_publish_swap_t)*ebpf_nprocs;
140 + while (pids) {
141 + key = pids->pid;
142 +
143 + if (bpf_map_lookup_elem(fd, &key, cv)) {
144 + pids = pids->next;
145 + continue;
146 + }
147 +
148 + swap_apps_accumulator(cv);
149 +
150 + swap_fill_pid(key, cv);
151 +
152 + // We are cleaning to avoid passing data read from one process to other.
153 + memset(cv, 0, length);
154 +
155 + pids = pids->next;
156 + }
157 +}
158 +
159 +/**
160 +* Send global
161 +*
162 +* Send global charts to Netdata
163 +*/
164 +static void swap_send_global()
165 +{
166 + write_io_chart(NETDATA_MEM_SWAP_CHART, NETDATA_EBPF_SYSTEM_GROUP,
167 + swap_publish_aggregated[NETDATA_KEY_SWAP_WRITEPAGE_CALL].dimension,
168 + (long long) swap_hash_values[NETDATA_KEY_SWAP_WRITEPAGE_CALL],
169 + swap_publish_aggregated[NETDATA_KEY_SWAP_READPAGE_CALL].dimension,
170 + (long long) swap_hash_values[NETDATA_KEY_SWAP_READPAGE_CALL]);
171 +}
172 +
173 +/**
174 + * Read global counter
175 + *
176 + * Read the table with number of calls for all functions
177 + */
178 +static void read_global_table()
179 +{
180 + uint64_t stored;
181 + netdata_idx_t *val = swap_hash_values;
182 + int fd = map_fd[NETDATA_SWAP_GLOBAL_TABLE];
183 +
184 + uint32_t i, end = NETDATA_SWAP_END;
185 + for (i = NETDATA_KEY_SWAP_READPAGE_CALL; i < end; i++) {
186 + if (!bpf_map_lookup_elem(fd, &i, &stored)) {
187 + val[i] = stored;
188 + }
189 + }
190 +}
191 +
192 +/**
193 + * Socket read hash
194 + *
195 + * This is the thread callback.
196 + * This thread is necessary, because we cannot freeze the whole plugin to read the data on very busy socket.
197 + *
198 + * @param ptr It is a NULL value for this thread.
199 + *
200 + * @return It always returns NULL.
201 + */
202 +void *ebpf_swap_read_hash(void *ptr)
203 +{
204 + read_thread_closed = 0;
205 +
206 + heartbeat_t hb;
207 + heartbeat_init(&hb);
208 +
209 + ebpf_module_t *em = (ebpf_module_t *)ptr;
210 + usec_t step = NETDATA_SWAP_SLEEP_MS * em->update_time;
211 + while (!close_ebpf_plugin) {
212 + usec_t dt = heartbeat_next(&hb, step);
213 + (void)dt;
214 +
215 + read_global_table();
216 + }
217 +
218 + read_thread_closed = 1;
219 + return NULL;
220 +}
221 +
222 +/**
223 + * Sum PIDs
224 + *
225 + * Sum values for all targets.
226 + *
227 + * @param swap
228 + * @param root
229 + */
230 +static void ebpf_swap_sum_pids(netdata_publish_swap_t *swap, struct pid_on_target *root)
231 +{
232 + uint64_t local_read = 0;
233 + uint64_t local_write = 0;
234 +
235 + while (root) {
236 + int32_t pid = root->pid;
237 + netdata_publish_swap_t *w = swap_pid[pid];
238 + if (w) {
239 + local_write += w->write;
240 + local_read += w->read;
241 + }
242 + root = root->next;
243 + }
244 +
245 + // These conditions were added, because we are using incremental algorithm
246 + swap->write = (local_write >= swap->write) ? local_write : swap->write;
247 + swap->read = (local_read >= swap->read) ? local_read : swap->read;
248 +}
249 +
250 +/**
251 + * Send data to Netdata calling auxiliar functions.
252 + *
253 + * @param root the target list.
254 +*/
255 +void ebpf_swap_send_apps_data(struct target *root)
256 +{
257 + struct target *w;
258 + for (w = root; w; w = w->next) {
259 + if (unlikely(w->exposed && w->processes)) {
260 + ebpf_swap_sum_pids(&w->swap, w->root_pid);
261 + }
262 + }
263 +
264 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_MEM_SWAP_READ_CHART);
265 + for (w = root; w; w = w->next) {
266 + if (unlikely(w->exposed && w->processes)) {
267 + write_chart_dimension(w->name, (long long) w->swap.read);
268 + }
269 + }
270 + write_end_chart();
271 +
272 + write_begin_chart(NETDATA_APPS_FAMILY, NETDATA_MEM_SWAP_WRITE_CHART);
273 + for (w = root; w; w = w->next) {
274 + if (unlikely(w->exposed && w->processes)) {
275 + write_chart_dimension(w->name, (long long) w->swap.write);
276 + }
277 + }
278 + write_end_chart();
279 +}
280 +
281 +/**
282 +* Main loop for this collector.
283 +*/
284 +static void swap_collector(ebpf_module_t *em)
285 +{
286 + swap_threads.thread = mallocz(sizeof(netdata_thread_t));
287 + swap_threads.start_routine = ebpf_swap_read_hash;
288 +
289 + map_fd = swap_data.map_fd;
290 +
291 + netdata_thread_create(swap_threads.thread, swap_threads.name, NETDATA_THREAD_OPTION_JOINABLE,
292 + ebpf_swap_read_hash, em);
293 +
294 + int apps = em->apps_charts;
295 + while (!close_ebpf_plugin) {
296 + pthread_mutex_lock(&collect_data_mutex);
297 + pthread_cond_wait(&collect_data_cond_var, &collect_data_mutex);
298 +
299 + if (apps)
300 + read_apps_table();
301 +
302 + pthread_mutex_lock(&lock);
303 +
304 + swap_send_global();
305 +
306 + if (apps)
307 + ebpf_swap_send_apps_data(apps_groups_root_target);
308 +
309 + pthread_mutex_unlock(&lock);
310 + pthread_mutex_unlock(&collect_data_mutex);
311 + }
312 +}
313 +
314 +/*****************************************************************
315 + *
316 + * INITIALIZE THREAD
317 + *
318 + *****************************************************************/
319 +
320 +/**
321 + * Create apps charts
322 + *
323 + * Call ebpf_create_chart to create the charts on apps submenu.
324 + *
325 + * @param em a pointer to the structure with the default values.
326 + */
327 +void ebpf_swap_create_apps_charts(struct ebpf_module *em, void *ptr)
328 +{
329 + UNUSED(em);
330 +
331 + struct target *root = ptr;
332 + ebpf_create_charts_on_apps(NETDATA_MEM_SWAP_READ_CHART,
333 + "Calls for function <code>swap_readpage</code>.",
334 + EBPF_COMMON_DIMENSION_CALL,
335 + NETDATA_SWAP_SUBMENU,
336 + NETDATA_EBPF_CHART_TYPE_STACKED,
337 + 20191,
338 + ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
339 + root);
340 +
341 + ebpf_create_charts_on_apps(NETDATA_MEM_SWAP_WRITE_CHART,
342 + "Calls for function <code>swap_writepage</code>.",
343 + EBPF_COMMON_DIMENSION_CALL,
344 + NETDATA_SWAP_SUBMENU,
345 + NETDATA_EBPF_CHART_TYPE_STACKED,
346 + 20192,
347 + ebpf_algorithms[NETDATA_EBPF_INCREMENTAL_IDX],
348 + root);
349 +}
350 +
351 +/**
352 + * Allocate vectors used with this thread.
353 + *
354 + * We are not testing the return, because callocz does this and shutdown the software
355 + * case it was not possible to allocate.
356 + *
357 + * @param length is the length for the vectors used inside the collector.
358 + */
359 +static void ebpf_swap_allocate_global_vectors()
360 +{
361 + swap_pid = callocz((size_t)pid_max, sizeof(netdata_publish_swap_t *));
362 + swap_vector = callocz((size_t)ebpf_nprocs, sizeof(netdata_publish_swap_t));
363 +
364 + memset(swap_hash_values, 0, sizeof(swap_hash_values));
365 +}
366 +
367 +/*****************************************************************
368 + *
369 + * MAIN THREAD
370 + *
371 + *****************************************************************/
372 +
373 +/**
374 + * Create global charts
375 + *
376 + * Call ebpf_create_chart to create the charts for the collector.
377 + */
378 +static void ebpf_create_swap_charts()
379 +{
380 + ebpf_create_chart(NETDATA_EBPF_SYSTEM_GROUP, NETDATA_MEM_SWAP_CHART,
381 + "Calls for internal functions used to access swap.",
382 + EBPF_COMMON_DIMENSION_CALL, NETDATA_SYSTEM_SWAP_SUBMENU,
383 + NULL,
384 + NETDATA_EBPF_CHART_TYPE_LINE,
385 + 202,
386 + ebpf_create_global_dimension,
387 + swap_publish_aggregated, NETDATA_SWAP_END);
388 +}
389 +
390 +/**
391 + * SWAP thread
392 + *
393 + * Thread used to make swap thread
394 + *
395 + * @param ptr a pointer to `struct ebpf_module`
396 + *
397 + * @return It always return NULL
398 + */
399 +void *ebpf_swap_thread(void *ptr)
400 +{
401 + netdata_thread_cleanup_push(ebpf_swap_cleanup, ptr);
402 +
403 + ebpf_module_t *em = (ebpf_module_t *)ptr;
404 + em->maps = swap_maps;
405 + fill_ebpf_data(&swap_data);
406 +
407 + ebpf_update_module(em, &swap_config, NETDATA_DIRECTORY_SWAP_CONFIG_FILE);
408 + ebpf_update_pid_table(&swap_maps[0], em);
409 +
410 + if (!em->enabled)
411 + goto endswap;
412 +
413 + if (ebpf_update_kernel(&swap_data)) {
414 + goto endswap;
415 + }
416 +
417 + probe_links = ebpf_load_program(ebpf_plugin_dir, em, kernel_string, &objects, swap_data.map_fd);
418 + if (!probe_links) {
419 + goto endswap;
420 + }
421 +
422 + ebpf_swap_allocate_global_vectors();
423 +
424 + int algorithms[NETDATA_SWAP_END] = { NETDATA_EBPF_INCREMENTAL_IDX, NETDATA_EBPF_INCREMENTAL_IDX };
425 + ebpf_global_labels(swap_aggregated_data, swap_publish_aggregated, swap_dimension_name, swap_dimension_name,
426 + algorithms, NETDATA_SWAP_END);
427 +
428 + pthread_mutex_lock(&lock);
429 + ebpf_create_swap_charts();
430 + pthread_mutex_unlock(&lock);
431 +
432 + swap_collector(em);
433 +
434 +endswap:
435 + netdata_thread_cleanup_pop(1);
436 + return NULL;
437 +}
collectors/ebpf.plugin/ebpf_swap.h new
+41
@@ -0,0 +1,41 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_EBPF_SWAP_H
4 +#define NETDATA_EBPF_SWAP_H 1
5 +
6 +#define NETDATA_SWAP_SLEEP_MS 850000ULL
7 +
8 +// charts
9 +#define NETDATA_MEM_SWAP_CHART "swapcalls"
10 +#define NETDATA_MEM_SWAP_READ_CHART "swap_read_call"
11 +#define NETDATA_MEM_SWAP_WRITE_CHART "swap_write_call"
12 +#define NETDATA_SWAP_SUBMENU "swap (eBPF)"
13 +
14 +// configuration file
15 +#define NETDATA_DIRECTORY_SWAP_CONFIG_FILE "swap.conf"
16 +
17 +typedef struct netdata_publish_swap {
18 + uint64_t read;
19 + uint64_t write;
20 +} netdata_publish_swap_t;
21 +
22 +enum swap_tables {
23 + NETDATA_PID_SWAP_TABLE,
24 + NETDATA_SWAP_GLOBAL_TABLE
25 +};
26 +
27 +enum swap_counters {
28 + NETDATA_KEY_SWAP_READPAGE_CALL,
29 + NETDATA_KEY_SWAP_WRITEPAGE_CALL,
30 +
31 + // Keep this as last and don't skip numbers as it is used as element counter
32 + NETDATA_SWAP_END
33 +};
34 +
35 +extern netdata_publish_swap_t **swap_pid;
36 +
37 +extern void *ebpf_swap_thread(void *ptr);
38 +extern void ebpf_swap_create_apps_charts(struct ebpf_module *em, void *ptr);
39 +extern void clean_swap_pid_structures();
40 +
41 +#endif
packaging/ebpf.checksums
+3 -3
@@ -1,3 +1,3 @@
1 -1442027d53cf11e1b086ec837659a498a9a2738ef43e44b32a2a0171d057544a netdata-kernel-collector-glibc-v0.6.3.tar.xz
2 -0863b06e78bb3a596cb1f68d13560301f563683cb174fd27b1e34c232e6f3c22 netdata-kernel-collector-musl-v0.6.3.tar.xz
3 -571dddd2b3b06d9f53cc24384ffbd88e2bb662ad953acaef46c76249186fe3b6 netdata-kernel-collector-static-v0.6.3.tar.xz
1 +6102337e8d38c4902c02371b44e962b65d9d7f4e793fc0e093ec6352e3cf8b14 netdata-kernel-collector-glibc-v0.6.5.tar.xz
2 +12e95abfe9173566d20467b5946e5850c830533bc0ab46c0f95470c95e7ccc19 netdata-kernel-collector-musl-v0.6.5.tar.xz
3 +d26a976d684cc4635a530d17cf0caaa70223a80d795339923f8cc6ba55413741 netdata-kernel-collector-static-v0.6.5.tar.xz
packaging/ebpf.version
+1 -1
@@ -1 +1 @@
1 -v0.6.3
1 +v0.6.5
web/gui/dashboard_info.js
+8
@@ -3504,6 +3504,14 @@ netdataDashboard.context = {
3504 info: 'Difference between the number of process created and the number of threads created per period(<code>process</code> dimension), it also shows the number of possible zombie process running on system.'
3505 },
3506
3507 + 'apps.swap_read_call': {
3508 + info: 'The function <code>swap_readpage</code> is called when the kernel reads a page from swap memory.'
3509 + },
3510 +
3511 + 'apps.swap_write_call': {
3512 + info: 'The function <code>swap_writepage</code> is called when the kernel writes a page to swap memory.'
3513 + },
3514 +
3515 // ------------------------------------------------------------------------
3516 // ACLK Internal Stats
3517 'netdata.aclk_status': {