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': {