@cryptotaxi247 / netdata-1 / commits / 9f25a1ca2

eBPF modular (#9148)

Convert the monolithic ebpf.plugin in a modular plugin.

thiagoftsm committed May 28, 2020 at 15:47 UTC 9f25a1ca2fbf1edc03fab629ef4746bcf3858d37
12 files changed +1659 -921
CMakeLists.txt
+5
@@ -426,6 +426,11 @@ set(SLABINFO_PLUGIN_FILES
426
427 set(EBPF_PROCESS_PLUGIN_FILES
428 collectors/ebpf.plugin/ebpf.c
429 + collectors/ebpf.plugin/ebpf.h
430 + collectors/ebpf.plugin/ebpf_process.c
431 + collectors/ebpf.plugin/ebpf_process.h
432 + collectors/ebpf.plugin/ebpf_socket.c
433 + collectors/ebpf.plugin/ebpf_socket.h
434 )
435
436 set(PROC_PLUGIN_FILES
Makefile.am
+4
@@ -266,6 +266,10 @@ PERF_PLUGIN_FILES = \
266
267 EBPF_PLUGIN_FILES = \
268 collectors/ebpf.plugin/ebpf.c \
269 + collectors/ebpf.plugin/ebpf_process.c \
270 + collectors/ebpf.plugin/ebpf_process.h \
271 + collectors/ebpf.plugin/ebpf_socket.c \
272 + collectors/ebpf.plugin/ebpf_socket.h \
273 collectors/ebpf.plugin/ebpf.h \
274 $(LIBNETDATA_FILES) \
275 $(NULL)
collectors/ebpf.plugin/README.md
+3 -3
@@ -130,14 +130,14 @@ cd /etc/netdata/ # Replace with your Netdata configuration directory, if not /
130
131 The `[global]` section defines settings for the whole eBPF collector.
132
133 -#### load
133 +#### ebpf load mode
134
135 The collector has two different eBPF programs. These programs monitor the same functions inside the kernel, but they
136 monitor, process, and display different kinds of information.
137
138 By default, this plugin uses the `entry` mode. Changing this mode can create significant overhead on your operating
139 -system, but also offer valuable information if you are developing or debugging software. The `load` option accepts the
140 -following values: ​
139 +system, but also offer valuable information if you are developing or debugging software. The `ebpf load mode` option
140 +accepts the following values: ​
141
142 - `entry`: This is the default mode. In this mode, the eBPF collector only monitors calls for the functions described
143 in the sections above, and does not show charts related to errors.
collectors/ebpf.plugin/ebpf.c
+448 -858
@@ -35,409 +35,154 @@ void netdata_cleanup_and_exit(int ret) {
35 }
36
37 // ----------------------------------------------------------------------
38 -//Netdata eBPF library
39 -void *libnetdata = NULL;
40 -int (*load_bpf_file)(char *, int) = NULL;
41 -int (*set_bpf_perf_event)(int, int);
42 -int (*perf_event_unmap)(struct perf_event_mmap_page *, size_t);
43 -int (*perf_event_mmap_header)(int, struct perf_event_mmap_page **, int);
44 -void (*netdata_perf_loop_multi)(int *, struct perf_event_mmap_page **, int, int *, int (*nsb)(void *, int), int);
45 -int *map_fd = NULL;
46 -
47 -//Perf event variables
48 -static int pmu_fd[NETDATA_MAX_PROCESSOR];
49 -static struct perf_event_mmap_page *headers[NETDATA_MAX_PROCESSOR];
50 -int page_cnt = 8;
51 -
52 -//Libbpf (It is necessary to have at least kernel 4.10)
53 -int (*bpf_map_lookup_elem)(int, const void *, void *);
54 -
55 -static char *plugin_dir = PLUGINS_DIR;
56 -static char *user_config_dir = CONFIG_DIR;
57 -static char *stock_config_dir = LIBCONFIG_DIR;
58 -static char *netdata_configured_log_dir = LOG_DIR;
59 -
60 -FILE *developer_log = NULL;
61 -
62 -//Global vectors
63 -netdata_syscall_stat_t *aggregated_data = NULL;
64 -netdata_publish_syscall_t *publish_aggregated = NULL;
38 +char *ebpf_plugin_dir = PLUGINS_DIR;
39 +static char *ebpf_user_config_dir = CONFIG_DIR;
40 +static char *ebpf_stock_config_dir = LIBCONFIG_DIR;
41 +static char *ebpf_configured_log_dir = LOG_DIR;
42
43 static int update_every = 1;
44 static int thread_finished = 0;
68 -static int close_plugin = 0;
69 -static netdata_run_mode_t mode = MODE_ENTRY;
70 -static int debug_log = 0;
71 -static int use_stdout = 0;
72 -struct config collector_config;
73 -static int mykernel = 0;
74 -static char kernel_string[64];
75 -static int nprocs;
45 +int close_ebpf_plugin = 0;
46 +struct config collector_config = { .first_section = NULL, .last_section = NULL, .mutex = NETDATA_MUTEX_INITIALIZER,
47 + .index = { .avl_tree = { .root = NULL, .compar = appconfig_section_compare },
48 + .rwlock = AVL_LOCK_INITIALIZER } };
49 +
50 +int running_on_kernel = 0;
51 +char kernel_string[64];
52 +int ebpf_nprocs;
53 static int isrh;
54 netdata_idx_t *hash_values;
55
56 pthread_mutex_t lock;
57
81 -static struct ebpf_module {
82 - const char *thread_name;
83 - int enabled;
84 - void (*start_routine) (void *);
85 - int update_time;
86 - int global_charts;
87 - int apps_charts;
88 - netdata_run_mode_t mode;
89 -} ebpf_modules[] = {
90 - { .thread_name = "process", .enabled = 0, .start_routine = NULL, .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY },
91 - { .thread_name = "network_viewer", .enabled = 0, .start_routine = NULL, .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY },
92 - { .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_time = 1, .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY },
58 +netdata_ebpf_events_t process_probes[] = {
59 + { .type = 'r', .name = "vfs_write" },
60 + { .type = 'r', .name = "vfs_writev" },
61 + { .type = 'r', .name = "vfs_read" },
62 + { .type = 'r', .name = "vfs_readv" },
63 + { .type = 'r', .name = "do_sys_open" },
64 + { .type = 'r', .name = "vfs_unlink" },
65 + { .type = 'p', .name = "do_exit" },
66 + { .type = 'p', .name = "release_task" },
67 + { .type = 'r', .name = "_do_fork" },
68 + { .type = 'r', .name = "__close_fd" },
69 + { .type = 'r', .name = "__x64_sys_clone" },
70 + { .type = 0, .name = NULL }
71 };
72
95 -static char *dimension_names[NETDATA_MAX_MONITOR_VECTOR] = { "open", "close", "delete", "read", "write", "process", "task", "process", "thread" };
96 -static char *id_names[NETDATA_MAX_MONITOR_VECTOR] = { "do_sys_open", "__close_fd", "vfs_unlink", "vfs_read", "vfs_write", "do_exit", "release_task", "_do_fork", "sys_clone" };
97 -static char *status[] = { "process", "zombie" };
98 -
99 -int event_pid = 0;
100 -netdata_ebpf_events_t collector_events[] = {
101 - { .type = 'r', .name = "vfs_write" },
102 - { .type = 'r', .name = "vfs_writev" },
103 - { .type = 'r', .name = "vfs_read" },
104 - { .type = 'r', .name = "vfs_readv" },
105 - { .type = 'r', .name = "do_sys_open" },
106 - { .type = 'r', .name = "vfs_unlink" },
107 - { .type = 'p', .name = "do_exit" },
108 - { .type = 'p', .name = "release_task" },
109 - { .type = 'r', .name = "_do_fork" },
110 - { .type = 'r', .name = "__close_fd" },
111 - { .type = 'r', .name = "__x64_sys_clone" },
112 - { .type = 0, .name = NULL }
73 +netdata_ebpf_events_t socket_probes[] = {
74 + { .type = 'r', .name = "tcp_sendmsg" },
75 + { .type = 'p', .name = "tcp_cleanup_rbuf" },
76 + { .type = 'p', .name = "tcp_close" },
77 + { .type = 'p', .name = "udp_recvmsg" },
78 + { .type = 'r', .name = "udp_recvmsg" },
79 + { .type = 'r', .name = "udp_sendmsg" },
80 + { .type = 'p', .name = "do_exit" },
81 + { .type = 0, .name = NULL }
82 };
83
115 -void open_developer_log() {
116 - char filename[FILENAME_MAX+1];
117 - int tot = sprintf(filename, "%s/%s", netdata_configured_log_dir, NETDATA_DEVELOPER_LOG_FILE);
118 -
119 - if(tot > 0)
120 - developer_log = fopen(filename, "a");
121 -}
122 -
123 -static int unmap_memory() {
124 - int i;
125 - int size = (int)sysconf(_SC_PAGESIZE)*(page_cnt + 1);
126 - for ( i = 0 ; i < nprocs ; i++ ) {
127 - if (perf_event_unmap(headers[i], size) < 0) {
128 - fprintf(stderr,"[EBPF PROCESS] CANNOT unmap headers.\n");
129 - return -1;
130 - }
131 -
132 - close(pmu_fd[i]);
133 - }
134 -
135 - return 0;
136 -}
84 +ebpf_module_t ebpf_modules[] = {
85 + { .thread_name = "process", .config_name = "process", .enabled = 0, .start_routine = ebpf_process_thread,
86 + .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY, .probes = process_probes },
87 + { .thread_name = "socket", .config_name = "network viewer", .enabled = 0, .start_routine = ebpf_socket_thread,
88 + .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY, .probes = socket_probes },
89 + { .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_time = 1,
90 + .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY, .probes = NULL },
91 +};
92
138 -static void int_exit(int sig)
93 +/**
94 + * Close the collector gracefully
95 + *
96 + * @param sig is the signal number used to close the collector
97 + */
98 +static void ebpf_exit(int sig)
99 {
140 - close_plugin = 1;
100 + int event_pid;
101 + close_ebpf_plugin = 1;
102
103 //When both threads were not finished case I try to go in front this address, the collector will crash
104 if (!thread_finished) {
105 return;
106 }
107
147 - if (aggregated_data) {
148 - free(aggregated_data);
149 - aggregated_data = NULL;
150 - }
151 -
152 - if (publish_aggregated) {
153 - free(publish_aggregated);
154 - publish_aggregated = NULL;
155 - }
156 -
157 - if(mode == MODE_DEVMODE && debug_log) {
158 - unmap_memory();
159 - }
160 -
161 - if (libnetdata) {
162 - dlclose(libnetdata);
163 - libnetdata = NULL;
164 - }
165 -
166 - if (developer_log) {
167 - fclose(developer_log);
168 - developer_log = NULL;
169 - }
170 -
171 - if (hash_values) {
172 - freez(hash_values);
173 - }
174 -
175 - if (event_pid) {
176 - int ret = fork();
177 - if (ret < 0) //error
178 - error("[EBPF PROCESS] Cannot fork(), so I won't be able to clean %skprobe_events", NETDATA_DEBUGFS);
179 - else if (!ret) { //child
180 - int i;
181 - for ( i=getdtablesize(); i>=0; --i)
182 - close(i);
183 -
184 - int fd = open("/dev/null",O_RDWR, 0);
185 - if (fd != -1) {
186 - dup2 (fd, STDIN_FILENO);
187 - dup2 (fd, STDOUT_FILENO);
188 - dup2 (fd, STDERR_FILENO);
189 - }
190 -
191 - if (fd > 2)
192 - close (fd);
193 -
194 - int sid = setsid();
195 - if(sid >= 0) {
196 - sleep(1);
197 - if(debug_log) {
198 - open_developer_log();
199 - }
200 - debug(D_EXIT, "Wait for father %d die", event_pid);
201 - clean_kprobe_events(developer_log, event_pid, collector_events);
202 - } else {
203 - error("Cannot become session id leader, so I won't try to clean kprobe_events.\n");
204 - }
205 - } else { //parent
206 - exit(0);
207 - }
208 -
209 - if (developer_log) {
210 - fclose(developer_log);
211 - developer_log = NULL;
108 + event_pid = getpid();
109 + int ret = fork();
110 + if (ret < 0) //error
111 + error("Cannot fork(), so I won't be able to clean %skprobe_events", NETDATA_DEBUGFS);
112 + else if (!ret) { //child
113 + int i;
114 + for ( i=getdtablesize(); i>=0; --i)
115 + close(i);
116 +
117 + int fd = open("/dev/null",O_RDWR, 0);
118 + if (fd != -1) {
119 + dup2 (fd, STDIN_FILENO);
120 + dup2 (fd, STDOUT_FILENO);
121 + dup2 (fd, STDERR_FILENO);
122 }
213 - }
214 -
215 - exit(sig);
216 -}
217 -
218 -static inline void netdata_write_chart_cmd(char *type
219 - , char *id
220 - , char *axis
221 - , char *web
222 - , int order)
223 -{
224 - printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
225 - , type
226 - , id
227 - , axis
228 - , web
229 - , order);
230 -}
231 -
232 -static void netdata_write_global_dimension(char *d, char *n)
233 -{
234 - printf("DIMENSION %s %s absolute 1 1\n", d, n);
235 -}
236 -
237 -static void netdata_create_global_dimension(void *ptr, int end)
238 -{
239 - netdata_publish_syscall_t *move = ptr;
240 -
241 - int i = 0;
242 - while (move && i < end) {
243 - netdata_write_global_dimension(move->name, move->dimension);
244 -
245 - move = move->next;
246 - i++;
247 - }
248 -}
249 -static inline void netdata_create_chart(char *family
250 - , char *name
251 - , char *axis
252 - , char *web
253 - , int order
254 - , void (*ncd)(void *, int)
255 - , void *move
256 - , int end)
257 -{
258 - netdata_write_chart_cmd(family, name, axis, web, order);
123
260 - ncd(move, end);
261 -}
262 -
263 -static void netdata_create_io_chart(char *family, char *name, char *axis, char *web, int order) {
264 - printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
265 - , family
266 - , name
267 - , axis
268 - , web
269 - , order);
270 -
271 - printf("DIMENSION %s %s absolute 1 1\n", id_names[3], NETDATA_VFS_DIM_OUT_FILE_BYTES);
272 - printf("DIMENSION %s %s absolute 1 1\n", id_names[4], NETDATA_VFS_DIM_IN_FILE_BYTES);
273 -}
274 -
275 -static void netdata_process_status_chart(char *family, char *name, char *axis, char *web, int order) {
276 - printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
277 - , family
278 - , name
279 - , axis
280 - , web
281 - , order);
282 -
283 - printf("DIMENSION %s '' absolute 1 1\n", status[0]);
284 - printf("DIMENSION %s '' absolute 1 1\n", status[1]);
285 -}
286 -
287 -static void netdata_global_charts_create() {
288 - netdata_create_chart(NETDATA_EBPF_FAMILY
289 - , NETDATA_FILE_OPEN_CLOSE_COUNT
290 - , "Calls"
291 - , NETDATA_FILE_GROUP
292 - , 970
293 - , netdata_create_global_dimension
294 - , publish_aggregated
295 - , 2);
296 -
297 - if(mode < MODE_ENTRY) {
298 - netdata_create_chart(NETDATA_EBPF_FAMILY
299 - , NETDATA_FILE_OPEN_ERR_COUNT
300 - , "Calls"
301 - , NETDATA_FILE_GROUP
302 - , 971
303 - , netdata_create_global_dimension
304 - , publish_aggregated
305 - , 2);
306 - }
124 + if (fd > 2)
125 + close (fd);
126
308 - netdata_create_chart(NETDATA_EBPF_FAMILY
309 - , NETDATA_VFS_FILE_CLEAN_COUNT
310 - , "Calls"
311 - , NETDATA_VFS_GROUP
312 - , 972
313 - , netdata_create_global_dimension
314 - , &publish_aggregated[NETDATA_DEL_START]
315 - , 1);
316 -
317 - netdata_create_chart(NETDATA_EBPF_FAMILY
318 - , NETDATA_VFS_FILE_IO_COUNT
319 - , "Calls"
320 - , NETDATA_VFS_GROUP
321 - , 973
322 - , netdata_create_global_dimension
323 - , &publish_aggregated[NETDATA_IN_START_BYTE]
324 - , 2);
325 -
326 - if(mode < MODE_ENTRY) {
327 - netdata_create_io_chart(NETDATA_EBPF_FAMILY
328 - , NETDATA_VFS_IO_FILE_BYTES
329 - , "bytes/s"
330 - , NETDATA_VFS_GROUP
331 - , 974);
332 -
333 - netdata_create_chart(NETDATA_EBPF_FAMILY
334 - , NETDATA_VFS_FILE_ERR_COUNT
335 - , "Calls"
336 - , NETDATA_VFS_GROUP
337 - , 975
338 - , netdata_create_global_dimension
339 - , &publish_aggregated[2]
340 - , NETDATA_VFS_ERRORS);
127 + int sid = setsid();
128 + if(sid >= 0) {
129 + sleep(1);
130 + debug(D_EXIT, "Wait for father %d die", event_pid);
131
342 - }
343 -
344 - netdata_create_chart(NETDATA_EBPF_FAMILY
345 - , NETDATA_PROCESS_SYSCALL
346 - , "Calls"
347 - , NETDATA_PROCESS_GROUP
348 - , 976
349 - , netdata_create_global_dimension
350 - , &publish_aggregated[NETDATA_PROCESS_START]
351 - , 2);
352 -
353 - netdata_create_chart(NETDATA_EBPF_FAMILY
354 - , NETDATA_EXIT_SYSCALL
355 - , "Calls"
356 - , NETDATA_PROCESS_GROUP
357 - , 977
358 - , netdata_create_global_dimension
359 - , &publish_aggregated[NETDATA_EXIT_START]
360 - , 2);
361 -
362 - netdata_process_status_chart(NETDATA_EBPF_FAMILY
363 - , NETDATA_PROCESS_STATUS_NAME
364 - , "Total"
365 - , NETDATA_PROCESS_GROUP
366 - , 978);
367 -
368 - if(mode < MODE_ENTRY) {
369 - netdata_create_chart(NETDATA_EBPF_FAMILY
370 - , NETDATA_PROCESS_ERROR_NAME
371 - , "Calls"
372 - , NETDATA_PROCESS_GROUP
373 - , 979
374 - , netdata_create_global_dimension
375 - , &publish_aggregated[NETDATA_PROCESS_START]
376 - , 2);
377 - }
378 -
379 -}
380 -
381 -
382 -static void netdata_create_charts() {
383 - netdata_global_charts_create();
384 -}
385 -
386 -static void netdata_update_publish(netdata_publish_syscall_t *publish
387 - , netdata_publish_vfs_common_t *pvc
388 - , netdata_syscall_stat_t *input) {
389 -
390 - netdata_publish_syscall_t *move = publish;
391 - while(move) {
392 - if(input->call != move->pcall) {
393 - //This condition happens to avoid initial values with dimensions higher than normal values.
394 - if(move->pcall) {
395 - move->ncall = (input->call > move->pcall)?input->call - move->pcall: move->pcall - input->call;
396 - move->nbyte = (input->bytes > move->pbyte)?input->bytes - move->pbyte: move->pbyte - input->bytes;
397 - move->nerr = (input->ecall > move->nerr)?input->ecall - move->perr: move->perr - input->ecall;
398 - } else {
399 - move->ncall = 0;
400 - move->nbyte = 0;
401 - move->nerr = 0;
402 - }
403 -
404 - move->pcall = input->call;
405 - move->pbyte = input->bytes;
406 - move->perr = input->ecall;
132 + for (event_pid = 0; ebpf_modules[event_pid].probes; event_pid++)
133 + clean_kprobe_events(NULL, (int)ebpf_modules[event_pid].thread_id, ebpf_modules[event_pid].probes);
134 } else {
408 - move->ncall = 0;
409 - move->nbyte = 0;
410 - move->nerr = 0;
135 + error("Cannot become session id leader, so I won't try to clean kprobe_events.\n");
136 }
412 -
413 - input = input->next;
414 - move = move->next;
137 + } else { //parent
138 + exit(0);
139 }
140
417 - pvc->write = -((long)publish[2].nbyte);
418 - pvc->read = (long)publish[3].nbyte;
419 -
420 - pvc->running = (long)publish[7].ncall - (long)publish[8].ncall;
421 - publish[6].ncall = -publish[6].ncall; // release
422 - pvc->zombie = (long)publish[5].ncall + (long)publish[6].ncall;
141 + exit(sig);
142 }
143
425 -static inline void write_begin_chart(char *family, char *name)
144 +/*****************************************************************
145 + *
146 + * FUNCTIONS TO CREATE CHARTS
147 + *
148 + *****************************************************************/
149 +
150 +/**
151 + * Write begin command on standard output
152 + *
153 + * @param family the chart family name
154 + * @param name the chart name
155 + */
156 +void write_begin_chart(char *family, char *name)
157 {
158 int ret = printf( "BEGIN %s.%s\n"
428 - , family
429 - , name);
159 + , family
160 + , name);
161
162 (void)ret;
163 }
164
434 -static inline void write_chart_dimension(char *dim, long long value)
165 +/**
166 + * Write set command on standard output
167 + *
168 + * @param dim the dimension name
169 + * @param value the value for the dimension
170 + */
171 +void write_chart_dimension(char *dim, long long value)
172 {
173 int ret = printf("SET %s = %lld\n", dim, value);
174 (void)ret;
175 }
176
440 -static void write_global_count_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
177 +/**
178 + * Call the necessary functions to create a chart.
179 + *
180 + * @param name the chart name
181 + * @param family the chart family
182 + * @param move the pointer with the values that will be published
183 + * @param end the number of values that will be written on standard output
184 + */
185 +void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
186 write_begin_chart(family, name);
187
188 int i = 0;
@@ -451,7 +196,15 @@ static void write_global_count_chart(char *name, char *family, netdata_publish_s
196 printf("END\n");
197 }
198
454 -static void write_global_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
199 +/**
200 + * Call the necessary functions to create a chart.
201 + *
202 + * @param name the chart name
203 + * @param family the chart family
204 + * @param move the pointer with the values that will be published
205 + * @param end the number of values that will be written on standard output
206 + */
207 +void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
208 write_begin_chart(family, name);
209
210 int i = 0;
@@ -465,176 +218,129 @@ static void write_global_err_chart(char *name, char *family, netdata_publish_sys
218 printf("END\n");
219 }
220
468 -static void write_io_chart(char *family, netdata_publish_vfs_common_t *pvc) {
469 - write_begin_chart(family, NETDATA_VFS_IO_FILE_BYTES);
470 -
471 - write_chart_dimension(id_names[3], (long long) pvc->write);
472 - write_chart_dimension(id_names[4], (long long) pvc->read);
473 -
474 - printf("END\n");
475 -}
221
477 -static void write_status_chart(char *family, netdata_publish_vfs_common_t *pvc) {
478 - write_begin_chart(family, NETDATA_PROCESS_STATUS_NAME);
222 +/**
223 + * Call the necessary functions to create a chart.
224 + *
225 + * @param family the chart family
226 + * @param move the pointer with the values that will be published
227 + */
228 +void write_io_chart(char *chart, char *family, char *dwrite, char *dread, netdata_publish_vfs_common_t *pvc) {
229 + write_begin_chart(family, chart);
230
480 - write_chart_dimension(status[0], (long long) pvc->running);
481 - write_chart_dimension(status[1], (long long) pvc->zombie);
231 + write_chart_dimension(dwrite, (long long) pvc->write);
232 + write_chart_dimension(dread, (long long) pvc->read);
233
234 printf("END\n");
235 }
236
486 -static void netdata_publish_data() {
487 - netdata_publish_vfs_common_t pvc;
488 - netdata_update_publish(publish_aggregated, &pvc, aggregated_data);
489 -
490 - write_global_count_chart(NETDATA_FILE_OPEN_CLOSE_COUNT, NETDATA_EBPF_FAMILY, publish_aggregated, 2);
491 - write_global_count_chart(NETDATA_VFS_FILE_CLEAN_COUNT, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_DEL_START], 1);
492 - write_global_count_chart(NETDATA_VFS_FILE_IO_COUNT, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_IN_START_BYTE], 2);
493 - write_global_count_chart(NETDATA_EXIT_SYSCALL, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_EXIT_START], 2);
494 - write_global_count_chart(NETDATA_PROCESS_SYSCALL, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_PROCESS_START], 2);
495 -
496 - write_status_chart(NETDATA_EBPF_FAMILY, &pvc);
497 - if(mode < MODE_ENTRY) {
498 - write_global_err_chart(NETDATA_FILE_OPEN_ERR_COUNT, NETDATA_EBPF_FAMILY, publish_aggregated, 2);
499 - write_global_err_chart(NETDATA_VFS_FILE_ERR_COUNT, NETDATA_EBPF_FAMILY, &publish_aggregated[2], NETDATA_VFS_ERRORS);
500 - write_global_err_chart(NETDATA_PROCESS_ERROR_NAME, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_PROCESS_START], 2);
501 -
502 - write_io_chart(NETDATA_EBPF_FAMILY, &pvc);
503 - }
504 -}
505 -
506 -void *process_publisher(void *ptr)
237 +/**
238 + * Write chart cmd on standard output
239 + *
240 + * @param type the chart type
241 + * @param id the chart id
242 + * @param axis the axis label
243 + * @param web the group name used to attach the chart on dashaboard
244 + * @param order the chart order
245 + */
246 +void ebpf_write_chart_cmd(char *type
247 + , char *id
248 + , char *axis
249 + , char *web
250 + , int order)
251 {
508 - (void)ptr;
509 - netdata_create_charts();
510 -
511 - usec_t step = update_every * USEC_PER_SEC;
512 - heartbeat_t hb;
513 - heartbeat_init(&hb);
514 - while(!close_plugin) {
515 - usec_t dt = heartbeat_next(&hb, step);
516 - (void)dt;
517 -
518 - pthread_mutex_lock(&lock);
519 - netdata_publish_data();
520 - pthread_mutex_unlock(&lock);
521 -
522 - fflush(stdout);
523 - }
524 -
525 - return NULL;
526 -}
527 -
528 -static void move_from_kernel2user_global() {
529 - uint64_t idx;
530 - netdata_idx_t res[NETDATA_GLOBAL_VECTOR];
531 -
532 - netdata_idx_t *val = hash_values;
533 - for (idx = 0; idx < NETDATA_GLOBAL_VECTOR; idx++) {
534 - if(!bpf_map_lookup_elem(map_fd[1], &idx, val)) {
535 - uint64_t total = 0;
536 - int i;
537 - int end = (mykernel < NETDATA_KERNEL_V4_15)?1:nprocs;
538 - for (i = 0; i < end; i++)
539 - total += val[i];
540 -
541 - res[idx] = total;
542 - } else {
543 - res[idx] = 0;
544 - }
545 - }
546 -
547 - aggregated_data[0].call = res[NETDATA_KEY_CALLS_DO_SYS_OPEN];
548 - aggregated_data[1].call = res[NETDATA_KEY_CALLS_CLOSE_FD];
549 - aggregated_data[2].call = res[NETDATA_KEY_CALLS_VFS_UNLINK];
550 - aggregated_data[3].call = res[NETDATA_KEY_CALLS_VFS_READ] + res[NETDATA_KEY_CALLS_VFS_READV];
551 - aggregated_data[4].call = res[NETDATA_KEY_CALLS_VFS_WRITE] + res[NETDATA_KEY_CALLS_VFS_WRITEV];
552 - aggregated_data[5].call = res[NETDATA_KEY_CALLS_DO_EXIT];
553 - aggregated_data[6].call = res[NETDATA_KEY_CALLS_RELEASE_TASK];
554 - aggregated_data[7].call = res[NETDATA_KEY_CALLS_DO_FORK];
555 - aggregated_data[8].call = res[NETDATA_KEY_CALLS_SYS_CLONE];
556 -
557 - aggregated_data[0].ecall = res[NETDATA_KEY_ERROR_DO_SYS_OPEN];
558 - aggregated_data[1].ecall = res[NETDATA_KEY_ERROR_CLOSE_FD];
559 - aggregated_data[2].ecall = res[NETDATA_KEY_ERROR_VFS_UNLINK];
560 - aggregated_data[3].ecall = res[NETDATA_KEY_ERROR_VFS_READ] + res[NETDATA_KEY_ERROR_VFS_READV];
561 - aggregated_data[4].ecall = res[NETDATA_KEY_ERROR_VFS_WRITE] + res[NETDATA_KEY_ERROR_VFS_WRITEV];
562 - aggregated_data[7].ecall = res[NETDATA_KEY_ERROR_DO_FORK];
563 - aggregated_data[8].ecall = res[NETDATA_KEY_ERROR_SYS_CLONE];
564 -
565 - aggregated_data[2].bytes = (uint64_t)res[NETDATA_KEY_BYTES_VFS_WRITE] + (uint64_t)res[NETDATA_KEY_BYTES_VFS_WRITEV];
566 - aggregated_data[3].bytes = (uint64_t)res[NETDATA_KEY_BYTES_VFS_READ] + (uint64_t)res[NETDATA_KEY_BYTES_VFS_READV];
567 -}
568 -
569 -static void move_from_kernel2user()
252 + printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
253 + , type
254 + , id
255 + , axis
256 + , web
257 + , order);
258 +}
259 +
260 +/**
261 + * Write the dimension command on standard output
262 + *
263 + * @param n the dimension name
264 + * @param d the dimension information
265 + */
266 +void ebpf_write_global_dimension(char *n, char *d)
267 {
571 - move_from_kernel2user_global();
268 + printf("DIMENSION %s %s absolute 1 1\n", n, d);
269 }
270
574 -void *process_collector(void *ptr)
271 +/**
272 + * Call ebpf_write_global_dimension to create the dimensions for a specific chart
273 + *
274 + * @param ptr a pointer to a structure of the type netdata_publish_syscall_t
275 + * @param end the number of dimensions for the structure ptr
276 + */
277 +void ebpf_create_global_dimension(void *ptr, int end)
278 {
576 - (void)ptr;
577 -
578 - usec_t step = 778879ULL;
579 - heartbeat_t hb;
580 - heartbeat_init(&hb);
581 - while(!close_plugin) {
582 - usec_t dt = heartbeat_next(&hb, step);
583 - (void)dt;
584 -
585 - pthread_mutex_lock(&lock);
586 - move_from_kernel2user();
587 - pthread_mutex_unlock(&lock);
588 - }
589 -
590 - return NULL;
591 -}
592 -
593 -static int netdata_store_bpf(void *data, int size) {
594 - (void)size;
595 -
596 - if (close_plugin)
597 - return 0;
598 -
599 - if(!debug_log)
600 - return -2; //LIBBPF_PERF_EVENT_CONT;
279 + netdata_publish_syscall_t *move = ptr;
280
602 - netdata_error_report_t *e = data;
603 - fprintf(developer_log
604 - ,"%llu %s %u: %s, %d\n"
605 - , now_realtime_usec() ,e->comm, e->pid, dimension_names[e->type], e->err);
606 - fflush(developer_log);
281 + int i = 0;
282 + while (move && i < end) {
283 + ebpf_write_global_dimension(move->name, move->dimension);
284
608 - return -2; //LIBBPF_PERF_EVENT_CONT;
285 + move = move->next;
286 + i++;
287 + }
288 }
289
611 -void *process_log(void *ptr)
290 +/**
291 + * Call write_chart_cmd to create the charts
292 + *
293 + * @param family the chart family
294 + * @param name the chart name
295 + * @param axis the axis label
296 + * @param web the group name used to attach the chart on dashaboard
297 + * @param order the order number of the specified chart
298 + * @param ncd a pointer to a function called to create dimensions
299 + * @param move a pointer for a structure that has the dimensions
300 + * @param end number of dimensions for the chart created
301 + */
302 +void ebpf_create_chart(char *family
303 + , char *name
304 + , char *axis
305 + , char *web
306 + , int order
307 + , void (*ncd)(void *, int)
308 + , void *move
309 + , int end)
310 {
613 - (void) ptr;
614 -
615 - if (mode == MODE_DEVMODE && debug_log) {
616 - netdata_perf_loop_multi(pmu_fd, headers, nprocs, &close_plugin, netdata_store_bpf, page_cnt);
617 - }
311 + ebpf_write_chart_cmd(family, name, axis, web, order);
312
619 - return NULL;
313 + ncd(move, end);
314 }
315
622 -void set_global_labels() {
316 +/*****************************************************************
317 + *
318 + * FUNCTIONS TO DEFINE OPTIONS
319 + *
320 + *****************************************************************/
321 +
322 +/**
323 + * Define labels used to generate charts
324 + *
325 + * @param is structure with information about number of calls made for a function.
326 + * @param pio structure used to generate charts.
327 + * @param dim a pointer for the dimensions name
328 + * @param name a pointer for the tensor with the name of the functions.
329 + * @param end the number of elements in the previous 4 arguments.
330 + */
331 +void ebpf_global_labels(netdata_syscall_stat_t *is, netdata_publish_syscall_t *pio, char **dim, char **name, int end) {
332 int i;
333
625 - netdata_syscall_stat_t *is = aggregated_data;
334 netdata_syscall_stat_t *prev = NULL;
627 -
628 - netdata_publish_syscall_t *pio = publish_aggregated;
335 netdata_publish_syscall_t *publish_prev = NULL;
630 - for (i = 0; i < NETDATA_MAX_MONITOR_VECTOR; i++) {
336 + for (i = 0; i < end; i++) {
337 if(prev) {
338 prev->next = &is[i];
339 }
340 prev = &is[i];
341
636 - pio[i].dimension = dimension_names[i];
637 - pio[i].name = id_names[i];
342 + pio[i].dimension = dim[i];
343 + pio[i].name = name[i];
344 if(publish_prev) {
345 publish_prev->next = &pio[i];
346 }
@@ -642,294 +348,37 @@ void set_global_labels() {
348 }
349 }
350
645 -int allocate_global_vectors() {
646 - aggregated_data = callocz(NETDATA_MAX_MONITOR_VECTOR, sizeof(netdata_syscall_stat_t));
647 - if(!aggregated_data) {
648 - return -1;
649 - }
650 -
651 - publish_aggregated = callocz(NETDATA_MAX_MONITOR_VECTOR, sizeof(netdata_publish_syscall_t));
652 - if(!publish_aggregated) {
653 - return -1;
654 - }
655 -
656 - hash_values = callocz(nprocs, sizeof(netdata_idx_t));
657 - if(!hash_values) {
658 - return -1;
659 - }
660 -
661 - return 0;
662 -}
663 -
664 -static void build_complete_path(char *out, size_t length,char *path, char *filename) {
665 - if(path){
666 - snprintf(out, length, "%s/%s", path, filename);
667 - } else {
668 - snprintf(out, length, "%s", filename);
669 - }
670 -}
671 -
672 -static int map_memory() {
673 - int i;
674 - for (i = 0; i < nprocs; i++) {
675 - pmu_fd[i] = set_bpf_perf_event(i, 2);
676 -
677 - if (perf_event_mmap_header(pmu_fd[i], &headers[i], page_cnt) < 0) {
678 - return -1;
679 - }
680 - }
681 - return 0;
682 -}
683 -
684 -static int ebpf_load_libraries()
685 -{
686 - char *err = NULL;
687 - char lpath[4096];
688 - char netdatasl[128];
689 - char *libbase = { "libnetdata_ebpf.so" };
690 -
691 - snprintf(netdatasl, 127, "%s.%s", libbase, kernel_string);
692 - build_complete_path(lpath, 4096, plugin_dir, netdatasl);
693 - libnetdata = dlopen(lpath, RTLD_LAZY);
694 - if (!libnetdata) {
695 - info("[EBPF_PROCESS] Cannot load library %s for the current kernel.", lpath);
696 -
697 - //Update kernel
698 - char *library = ebpf_library_suffix(mykernel, (isrh < 0)?0:1);
699 - size_t length = strlen(library);
700 - strncpyz(kernel_string, library, length);
701 - kernel_string[length] = '\0';
702 -
703 - //Try to load the default version
704 - snprintf(netdatasl, 127, "%s.%s", libbase, kernel_string);
705 - build_complete_path(lpath, 4096, plugin_dir, netdatasl);
706 - libnetdata = dlopen(lpath, RTLD_LAZY);
707 - if (!libnetdata) {
708 - error("[EBPF_PROCESS] Cannot load %s default library.", lpath);
709 - return -1;
710 - } else {
711 - info("[EBPF_PROCESS] Default shared library %s loaded with success.", lpath);
712 - }
713 - } else {
714 - info("[EBPF_PROCESS] Current shared library %s loaded with success.", lpath);
715 - }
716 -
717 - load_bpf_file = dlsym(libnetdata, "load_bpf_file");
718 - if ((err = dlerror()) != NULL) {
719 - error("[EBPF_PROCESS] Cannot find load_bpf_file: %s", err);
720 - return -1;
721 - }
722 -
723 - map_fd = dlsym(libnetdata, "map_fd");
724 - if ((err = dlerror()) != NULL) {
725 - error("[EBPF_PROCESS] Cannot find map_fd: %s", err);
726 - return -1;
727 - }
728 -
729 - bpf_map_lookup_elem = dlsym(libnetdata, "bpf_map_lookup_elem");
730 - if ((err = dlerror()) != NULL) {
731 - error("[EBPF_PROCESS] Cannot find bpf_map_lookup_elem: %s", err);
732 - return -1;
733 - }
734 -
735 - if(mode == 1) {
736 - set_bpf_perf_event = dlsym(libnetdata, "set_bpf_perf_event");
737 - if ((err = dlerror()) != NULL) {
738 - error("[EBPF_PROCESS] Cannot find set_bpf_perf_event: %s", err);
739 - return -1;
740 - }
741 -
742 - perf_event_unmap = dlsym(libnetdata, "perf_event_unmap");
743 - if ((err = dlerror()) != NULL) {
744 - error("[EBPF_PROCESS] Cannot find perf_event_unmap: %s", err);
745 - return -1;
746 - }
747 -
748 - perf_event_mmap_header = dlsym(libnetdata, "perf_event_mmap_header");
749 - if ((err = dlerror()) != NULL) {
750 - error("[EBPF_PROCESS] Cannot find perf_event_mmap_header: %s", err);
751 - return -1;
752 - }
753 -
754 - if(mode == MODE_DEVMODE) {
755 - set_bpf_perf_event = dlsym(libnetdata, "set_bpf_perf_event");
756 - if ((err = dlerror()) != NULL) {
757 - error("[EBPF_PROCESS] Cannot find set_bpf_perf_event: %s", err);
758 - return -1;
759 - }
760 -
761 - perf_event_unmap = dlsym(libnetdata, "perf_event_unmap");
762 - if ((err = dlerror()) != NULL) {
763 - error("[EBPF_PROCESS] Cannot find perf_event_unmap: %s", err);
764 - return -1;
765 - }
766 -
767 - perf_event_mmap_header = dlsym(libnetdata, "perf_event_mmap_header");
768 - if ((err = dlerror()) != NULL) {
769 - error("[EBPF_PROCESS] Cannot find perf_event_mmap_header: %s", err);
770 - return -1;
771 - }
772 -
773 - netdata_perf_loop_multi = dlsym(libnetdata, "netdata_perf_loop_multi");
774 - if ((err = dlerror()) != NULL) {
775 - error("[EBPF_PROCESS] Cannot find netdata_perf_loop_multi: %s", err);
776 - return -1;
777 - }
778 - }
779 - }
780 -
781 - return 0;
782 -}
783 -
784 -int select_file(char *name, int length) {
785 - int ret = -1;
786 - if (!mode)
787 - ret = snprintf(name, (size_t)length, "rnetdata_ebpf_process.%s.o", kernel_string);
788 - else if(mode == 1)
789 - ret = snprintf(name, (size_t)length, "dnetdata_ebpf_process.%s.o", kernel_string);
790 - else if(mode == 2)
791 - ret = snprintf(name, (size_t)length, "pnetdata_ebpf_process.%s.o", kernel_string);
792 -
793 - return ret;
794 -}
795 -
796 -int process_load_ebpf()
797 -{
798 - char lpath[4096];
799 - char name[128];
800 -
801 - int test = select_file(name, 127);
802 - if (test < 0 || test > 127)
803 - return -1;
804 -
805 - build_complete_path(lpath, 4096, plugin_dir, name);
806 - event_pid = getpid();
807 - if (load_bpf_file(lpath, event_pid)) {
808 - error("[EBPF_PROCESS] Cannot load program: %s", lpath);
809 - return -1;
810 - } else {
811 - info("[EBPF PROCESS]: The eBPF program %s was loaded with success.", name);
812 - }
813 -
814 - return 0;
815 -}
816 -
817 -void set_global_variables() {
818 - //Get environment variables
819 - plugin_dir = getenv("NETDATA_PLUGINS_DIR");
820 - if(!plugin_dir)
821 - plugin_dir = PLUGINS_DIR;
822 -
823 - user_config_dir = getenv("NETDATA_USER_CONFIG_DIR");
824 - if(!user_config_dir)
825 - user_config_dir = CONFIG_DIR;
826 -
827 - stock_config_dir = getenv("NETDATA_STOCK_CONFIG_DIR");
828 - if(!stock_config_dir)
829 - stock_config_dir = LIBCONFIG_DIR;
830 -
831 - netdata_configured_log_dir = getenv("NETDATA_LOG_DIR");
832 - if(!netdata_configured_log_dir)
833 - netdata_configured_log_dir = LOG_DIR;
834 -
835 - page_cnt *= (int)sysconf(_SC_NPROCESSORS_ONLN);
836 -
837 - nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
838 - if (nprocs > NETDATA_MAX_PROCESSOR) {
839 - nprocs = NETDATA_MAX_PROCESSOR;
840 - }
841 -
842 - isrh = get_redhat_release();
843 -}
844 -
845 -static void change_collector_event() {
846 - int i;
847 - if (mykernel < NETDATA_KERNEL_V5_3)
848 - collector_events[10].name = NULL;
849 -
850 - for (i = 0; collector_events[i].name ; i++ ) {
851 - collector_events[i].type = 'p';
852 - }
853 -}
854 -
855 -static void change_syscalls() {
856 - static char *lfork = { "do_fork" };
857 - id_names[7] = lfork;
858 - collector_events[8].name = lfork;
859 -}
860 -
861 -static inline void what_to_load(char *ptr) {
862 - if (!strcasecmp(ptr, "return"))
863 - mode = MODE_RETURN;
864 - /*
865 - else if (!strcasecmp(ptr, "dev"))
866 - mode = 1;
867 - */
868 - else
869 - change_collector_event();
870 -
871 - if (isrh >= NETDATA_MINIMUM_RH_VERSION && isrh < NETDATA_RH_8)
872 - change_syscalls();
873 -}
874 -
875 -static inline void enable_debug(char *ptr) {
876 - if (!strcasecmp(ptr, "yes"))
877 - debug_log = 1;
878 -}
879 -
880 -static inline void set_log_file(char *ptr) {
881 - if (!strcasecmp(ptr, "yes"))
882 - use_stdout = 1;
883 -}
884 -
885 -static void set_global_values() {
886 - struct section *sec = collector_config.first_section;
887 - while(sec) {
888 - if(!strcasecmp(sec->name, "global")) {
889 - struct config_option *values = sec->values;
890 - while(values) {
891 - if(!strcasecmp(values->name, "load"))
892 - what_to_load(values->value);
893 - else if(!strcasecmp(values->name, "debug log"))
894 - enable_debug(values->value);
895 - else if(!strcasecmp(values->name, "use stdout"))
896 - set_log_file(values->value);
897 -
898 - values = values->next;
899 - }
900 - }
901 - sec = sec->next;
902 - }
903 -}
904 -
905 -static int load_collector_file(char *path) {
906 - char lpath[4096];
907 -
908 - build_complete_path(lpath, 4096, path, "ebpf.conf" );
909 -
910 - if (!appconfig_load(&collector_config, lpath, 0, NULL))
911 - return 1;
912 -
913 - set_global_values();
914 -
915 - return 0;
916 -}
917 -
918 -static inline void ebpf_disable_apps() {
351 +/**
352 + * Define thread mode for all ebpf program.
353 + *
354 + * @param lmode the mode that will be used for them.
355 + */
356 +static inline void ebpf_set_thread_mode(netdata_run_mode_t lmode) {
357 int i ;
920 - for (i = 0 ;ebpf_modules[i].thread_name ; i++ ) {
921 - ebpf_modules[i].apps_charts = 0;
358 + for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
359 + ebpf_modules[i].mode = lmode;
360 }
361 }
362
925 -static inline void ebpf_enable_specific_chart(struct ebpf_module *em, int disable_apps) {
363 +/**
364 + * Enable specific charts selected by user.
365 + *
366 + * @param em the structure that will be changed
367 + * @param enable the status about the apps charts.
368 + */
369 +static inline void ebpf_enable_specific_chart(struct ebpf_module *em, int enable) {
370 em->enabled = 1;
927 - if (!disable_apps) {
371 + if (!enable) {
372 em->apps_charts = 1;
373 }
374 em->global_charts = 1;
375 }
376
377 +/**
378 + * Enable all charts
379 + *
380 + * @param apps what is the current status of apps
381 + */
382 static inline void ebpf_enable_all_charts(int apps) {
383 int i ;
384 for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
@@ -937,23 +386,37 @@ static inline void ebpf_enable_all_charts(int apps) {
386 }
387 }
388
940 -static inline void ebpf_enable_chart(int enable, int disable_apps) {
389 +/**
390 + * Enable the specified chart group
391 + *
392 + * @param idx the index of ebpf_modules that I am enabling
393 + * @param disable_apps should I keep apps charts?
394 + */
395 +static inline void ebpf_enable_chart(int idx, int disable_apps) {
396 int i ;
397 for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
943 - if (i == enable) {
398 + if (i == idx) {
399 ebpf_enable_specific_chart(&ebpf_modules[i], disable_apps);
400 break;
401 }
402 }
403 }
404
950 -static inline void ebpf_set_thread_mode(netdata_run_mode_t lmode) {
405 +/**
406 + * Disable APPs
407 + *
408 + * Disable charts for apps loading only global charts.
409 + */
410 +static inline void ebpf_disable_apps() {
411 int i ;
952 - for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
953 - ebpf_modules[i].mode = lmode;
412 + for (i = 0 ;ebpf_modules[i].thread_name ; i++ ) {
413 + ebpf_modules[i].apps_charts = 0;
414 }
415 }
416
417 +/**
418 + * Print help on standard error for user knows how to use the collector.
419 + */
420 void ebpf_print_help() {
421 const time_t t = time(NULL);
422 struct tm ct;
@@ -996,6 +459,151 @@ void ebpf_print_help() {
459 );
460 }
461
462 +/*****************************************************************
463 + *
464 + * AUXILIAR FUNCTIONS USED DURING INITIALIZATION
465 + *
466 + *****************************************************************/
467 +
468 +/**
469 + * Fill the ebpf_functions structure with default values
470 + *
471 + * @param ef the pointer to set default values
472 + */
473 +void fill_ebpf_functions(ebpf_functions_t *ef) {
474 + memset(ef, 0, sizeof(ebpf_functions_t));
475 + ef->kernel_string = kernel_string;
476 + ef->running_on_kernel = running_on_kernel;
477 + ef->map_fd = callocz(EBPF_MAX_MAPS, sizeof(int));
478 + ef->isrh = isrh;
479 +}
480 +
481 +/**
482 + * Define how to load the ebpf programs
483 + *
484 + * @param ptr the option given by users
485 + */
486 +static inline void how_to_load(char *ptr)
487 +{
488 + if (!strcasecmp(ptr, "return"))
489 + ebpf_set_thread_mode(MODE_RETURN);
490 + else if (!strcasecmp(ptr, "entry"))
491 + ebpf_set_thread_mode(MODE_ENTRY);
492 + else
493 + error("the option %s for \"ebpf load mode\" is not a valid option.", ptr);
494 +}
495 +
496 +/**
497 + * Parse disable apps option
498 + *
499 + * @param ptr the option given by users
500 + *
501 + * @return It returns 1 to disable the charts or 0 otherwise.
502 + */
503 +static inline int parse_disable_apps(char *ptr)
504 +{
505 + if (!strcasecmp(ptr, "yes")) {
506 + ebpf_disable_apps();
507 + return 1;
508 + } else if (strcasecmp(ptr, "no")) {
509 + error("The option %s for \"disable apps\" is not a valid option.", ptr);
510 + }
511 +
512 + return 0;
513 +}
514 +
515 +/**
516 + * Read collector values
517 + */
518 +static void read_collector_values() {
519 + // Read global section
520 + char *value;
521 + if (appconfig_exists(&collector_config, EBPF_GLOBAL_SECTION, "load")) //Backward compatibility
522 + value = appconfig_get(&collector_config, EBPF_GLOBAL_SECTION, "load", "entry");
523 + else
524 + value = appconfig_get(&collector_config, EBPF_GLOBAL_SECTION, "ebpf load mode", "entry");
525 +
526 + how_to_load(value);
527 +
528 + value = appconfig_get(&collector_config, EBPF_GLOBAL_SECTION, "disable apps", "no");
529 + int disable_apps = parse_disable_apps(value);
530 +
531 + // Read ebpf programs section
532 + uint32_t enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION,
533 + ebpf_modules[0].config_name, 1);
534 + int started = 0;
535 + if (enabled) {
536 + ebpf_enable_chart(0, disable_apps);
537 + started++;
538 + }
539 +
540 + enabled = appconfig_get_boolean(&collector_config, EBPF_PROGRAMS_SECTION,
541 + ebpf_modules[1].config_name, 1);
542 + if (enabled) {
543 + ebpf_enable_chart(1, disable_apps);
544 + started++;
545 + }
546 +
547 + if (!started)
548 + ebpf_enable_all_charts(disable_apps);
549 +
550 +}
551 +
552 +/**
553 + * Load collector config
554 + *
555 + * @param path the path where the file ebpf.conf is stored.
556 + *
557 + * @return 0 on success and -1 otherwise.
558 + */
559 +static int load_collector_config(char *path) {
560 + char lpath[4096];
561 +
562 + snprintf(lpath, 4095, "%s/%s", path, "ebpf.conf" );
563 +
564 + if (!appconfig_load(&collector_config, lpath, 0, NULL))
565 + return -1;
566 +
567 + read_collector_values();
568 +
569 + return 0;
570 +}
571 +
572 +/**
573 + * Set global variables reading environment variables
574 + */
575 +void set_global_variables() {
576 + //Get environment variables
577 + ebpf_plugin_dir = getenv("NETDATA_PLUGINS_DIR");
578 + if(!ebpf_plugin_dir)
579 + ebpf_plugin_dir = PLUGINS_DIR;
580 +
581 + ebpf_user_config_dir = getenv("NETDATA_USER_CONFIG_DIR");
582 + if(!ebpf_user_config_dir)
583 + ebpf_user_config_dir = CONFIG_DIR;
584 +
585 + ebpf_stock_config_dir = getenv("NETDATA_STOCK_CONFIG_DIR");
586 + if(!ebpf_stock_config_dir)
587 + ebpf_stock_config_dir = LIBCONFIG_DIR;
588 +
589 + ebpf_configured_log_dir = getenv("NETDATA_LOG_DIR");
590 + if(!ebpf_configured_log_dir)
591 + ebpf_configured_log_dir = LOG_DIR;
592 +
593 + ebpf_nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
594 + if (ebpf_nprocs > NETDATA_MAX_PROCESSOR) {
595 + ebpf_nprocs = NETDATA_MAX_PROCESSOR;
596 + }
597 +
598 + isrh = get_redhat_release();
599 +}
600 +
601 +/**
602 + * Parse arguments given from user.
603 + *
604 + * @param argc the number of arguments
605 + * @param argv the pointer to the arguments
606 + */
607 static void parse_args(int argc, char **argv)
608 {
609 int enabled = 0;
@@ -1067,8 +675,7 @@ static void parse_args(int argc, char **argv)
675 break;
676 }
677 case 'r': {
1070 - mode = MODE_RETURN;
1071 - ebpf_set_thread_mode(mode);
678 + ebpf_set_thread_mode(MODE_RETURN);
679 #ifdef NETDATA_INTERNAL_CHECKS
680 info("EBPF running in \"return\" mode, because it was started with the option \"--return\" or \"-r\".");
681 #endif
@@ -1084,6 +691,18 @@ static void parse_args(int argc, char **argv)
691 update_every = freq;
692 }
693
694 + if (load_collector_config(ebpf_user_config_dir)) {
695 + error("Does not have a configuration file inside `%s/ebpf.conf. It will try to load stock file.",
696 + ebpf_user_config_dir);
697 + if (load_collector_config(ebpf_stock_config_dir)) {
698 + error("Does not have a stock file. It is starting with default options.");
699 + } else {
700 + enabled = 1;
701 + }
702 + } else {
703 + enabled = 1;
704 + }
705 +
706 if (!enabled) {
707 ebpf_enable_all_charts(disable_apps);
708 #ifdef NETDATA_INTERNAL_CHECKS
@@ -1092,13 +711,29 @@ static void parse_args(int argc, char **argv)
711 }
712 }
713
714 +
715 +/*****************************************************************
716 + *
717 + * COLLECTOR ENTRY POINT
718 + *
719 + *****************************************************************/
720 +
721 +/**
722 + * Entry point
723 + *
724 + * @param argc the number of arguments
725 + * @param argv the pointer to the arguments
726 + *
727 + * @return it returns 0 on success and another integer otherwise
728 + */
729 int main(int argc, char **argv)
730 {
731 + set_global_variables();
732 parse_args(argc, argv);
733
1099 - mykernel = get_kernel_version(kernel_string, 63);
1100 - if(!has_condition_to_run(mykernel)) {
1101 - error("[EBPF PROCESS] The current collector cannot run on this kernel.");
734 + running_on_kernel = get_kernel_version(kernel_string, 63);
735 + if(!has_condition_to_run(running_on_kernel)) {
736 + error("The current collector cannot run on this kernel.");
737 return 1;
738 }
739
@@ -1114,87 +749,42 @@ int main(int argc, char **argv)
749
750 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
751 if (setrlimit(RLIMIT_MEMLOCK, &r)) {
1117 - error("[EBPF PROCESS] setrlimit(RLIMIT_MEMLOCK)");
752 + error("Setrlimit(RLIMIT_MEMLOCK)");
753 return 2;
754 }
755
1121 - set_global_variables();
1122 -
1123 - if (load_collector_file(user_config_dir)) {
1124 - info("[EBPF PROCESS] does not have a configuration file. It is starting with default options.");
1125 - change_collector_event();
1126 - if (isrh >= NETDATA_MINIMUM_RH_VERSION && isrh < NETDATA_RH_8)
1127 - change_syscalls();
1128 - }
1129 -
1130 - if(ebpf_load_libraries()) {
1131 - error("[EBPF_PROCESS] Cannot load library.");
1132 - thread_finished++;
1133 - int_exit(3);
1134 - }
1135 -
1136 - signal(SIGINT, int_exit);
1137 - signal(SIGTERM, int_exit);
1138 -
1139 - if (process_load_ebpf()) {
1140 - thread_finished++;
1141 - int_exit(4);
1142 - }
1143 -
1144 - if(allocate_global_vectors()) {
1145 - thread_finished++;
1146 - error("[EBPF_PROCESS] Cannot allocate necessary vectors.");
1147 - int_exit(5);
1148 - }
1149 -
1150 - if(mode == MODE_DEVMODE && debug_log) {
1151 - if(map_memory()) {
1152 - thread_finished++;
1153 - error("[EBPF_PROCESS] Cannot map memory used with perf events.");
1154 - int_exit(6);
1155 - }
1156 - }
1157 -
1158 - set_global_labels();
1159 -
1160 - if(debug_log) {
1161 - open_developer_log();
1162 - }
756 + signal(SIGINT, ebpf_exit);
757 + signal(SIGTERM, ebpf_exit);
758
759 if (pthread_mutex_init(&lock, NULL)) {
760 thread_finished++;
1166 - error("[EBPF PROCESS] Cannot start the mutex.");
1167 - int_exit(7);
761 + error("Cannot start the mutex.");
762 + ebpf_exit(3);
763 }
764
1170 - pthread_attr_t attr;
1171 - pthread_attr_init(&attr);
1172 - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
1173 - pthread_t thread[NETDATA_EBPF_PROCESS_THREADS];
765 + struct netdata_static_thread ebpf_threads[] = {
766 + {"EBPF PROCESS", NULL, NULL, 1, NULL, NULL, ebpf_modules[0].start_routine},
767 + {"EBPF SOCKET", NULL, NULL, 1, NULL, NULL, ebpf_modules[1].start_routine},
768 + {NULL, NULL, NULL, 0, NULL, NULL, NULL}
769 + };
770
771 int i;
1176 - int end = NETDATA_EBPF_PROCESS_THREADS;
1177 -
1178 - void * (*function_pointer[])(void *) = {process_publisher, process_collector, process_log };
772 + for (i = 0; ebpf_threads[i].name != NULL ; i++) {
773 + struct netdata_static_thread *st = &ebpf_threads[i];
774 + st->thread = mallocz(sizeof(netdata_thread_t));
775
1180 - for ( i = 0; i < end ; i++ ) {
1181 - if ( ( pthread_create(&thread[i], &attr, function_pointer[i], NULL) ) ) {
1182 - error("[EBPF_PROCESS] Cannot create threads.");
1183 - thread_finished++;
1184 - int_exit(8);
1185 - }
776 + ebpf_module_t *em = &ebpf_modules[i];
777 + em->thread_id = i;
778 + netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_JOINABLE, st->start_routine, em);
779 }
780
1188 - for ( i = 0; i < end ; i++ ) {
1189 - if ( (pthread_join(thread[i], NULL) ) ) {
1190 - error("[EBPF_PROCESS] Cannot join threads.");
1191 - thread_finished++;
1192 - int_exit(9);
1193 - }
781 + for (i = 0; ebpf_threads[i].name != NULL ; i++) {
782 + struct netdata_static_thread *st = &ebpf_threads[i];
783 + netdata_thread_join(*st->thread, NULL);
784 }
785
786 thread_finished++;
1197 - int_exit(0);
787 + ebpf_exit(0);
788
789 return 0;
790 }
collectors/ebpf.plugin/ebpf.conf
+6 -1
@@ -1,2 +1,7 @@
1 [global]
2 - load = entry
2 + ebpf load mode = entry
3 + disable apps = yes
4 +
5 +[ebpf programs]
6 + process = yes
7 + network viewer = yes
collectors/ebpf.plugin/ebpf.h
+62 -56
@@ -16,19 +16,6 @@
16 # include <unistd.h>
17 # include <dlfcn.h>
18
19 -# define NETDATA_GLOBAL_VECTOR 24
20 -# define NETDATA_MAX_MONITOR_VECTOR 9
21 -# define NETDATA_VFS_ERRORS 3
22 -# define NETDATA_PROCESS_ERRORS 4
23 -
24 -# define NETDATA_DEL_START 2
25 -# define NETDATA_IN_START_BYTE 3
26 -# define NETDATA_EXIT_START 5
27 -# define NETDATA_PROCESS_START 7
28 -# define NETDATA_PROCESS_RUNNING_COUNT 9
29 -
30 -# define NETDATA_EBPF_PROCESS_THREADS (uint32_t)3
31 -
19 # include <fcntl.h>
20 # include <ctype.h>
21 # include <dirent.h>
@@ -40,6 +27,7 @@
27 # include "../../libnetdata/clocks/clocks.h"
28 # include "../../libnetdata/config/appconfig.h"
29 # include "../../libnetdata/ebpf/ebpf.h"
30 +# include "../../daemon/main.h"
31
32 typedef enum {
33 MODE_RETURN = 0, //This attaches kprobe when the function returns
@@ -84,26 +72,21 @@ typedef struct netdata_error_report {
72 int err;
73 }netdata_error_report_t;
74
75 +typedef struct ebpf_module {
76 + const char *thread_name;
77 + const char *config_name;
78 + int enabled;
79 + void *(*start_routine) (void *);
80 + int update_time;
81 + int global_charts;
82 + int apps_charts;
83 + netdata_run_mode_t mode;
84 + netdata_ebpf_events_t *probes;
85 + uint32_t thread_id;
86 +} ebpf_module_t;
87 +
88 //Chart defintions
89 # define NETDATA_EBPF_FAMILY "ebpf"
89 -# define NETDATA_FILE_GROUP "File"
90 -# define NETDATA_VFS_GROUP "VFS"
91 -# define NETDATA_PROCESS_GROUP "Process"
92 -
93 -# define NETDATA_FILE_OPEN_CLOSE_COUNT "file_descriptor"
94 -# define NETDATA_FILE_OPEN_ERR_COUNT "file_error"
95 -# define NETDATA_VFS_FILE_CLEAN_COUNT "deleted_objects"
96 -# define NETDATA_VFS_FILE_IO_COUNT "io"
97 -# define NETDATA_VFS_FILE_ERR_COUNT "io_error"
98 -
99 -# define NETDATA_EXIT_SYSCALL "exit"
100 -# define NETDATA_PROCESS_SYSCALL "process_thread"
101 -# define NETDATA_PROCESS_ERROR_NAME "task_error"
102 -# define NETDATA_PROCESS_STATUS_NAME "process_status"
103 -
104 -# define NETDATA_VFS_IO_FILE_BYTES "io_bytes"
105 -# define NETDATA_VFS_DIM_IN_FILE_BYTES "write"
106 -# define NETDATA_VFS_DIM_OUT_FILE_BYTES "read"
90
91 //Log file
92 # define NETDATA_DEVELOPER_LOG_FILE "developer.log"
@@ -116,40 +99,63 @@ typedef struct netdata_error_report {
99 # define NETDATA_KERNEL_V5_3 328448
100 # define NETDATA_KERNEL_V4_15 265984
101
119 -//Index from kernel
120 -# define NETDATA_KEY_CALLS_DO_SYS_OPEN 0
121 -# define NETDATA_KEY_ERROR_DO_SYS_OPEN 1
102
123 -# define NETDATA_KEY_CALLS_VFS_WRITE 2
124 -# define NETDATA_KEY_ERROR_VFS_WRITE 3
125 -# define NETDATA_KEY_BYTES_VFS_WRITE 4
103 +# define EBPF_MAX_MAPS 32
104 +
105 +
106 +//Threads
107 +extern void *ebpf_process_thread(void *ptr);
108 +extern void *ebpf_socket_thread(void *ptr);
109 +
110 +//Common variables
111 +extern pthread_mutex_t lock;
112 +extern int close_ebpf_plugin;
113 +extern int ebpf_nprocs;
114 +extern int running_on_kernel;
115 +extern char *ebpf_plugin_dir;
116 +extern char kernel_string[64];
117 +extern netdata_ebpf_events_t process_probes[];
118 +extern netdata_ebpf_events_t socket_probes[];
119 +
120 +//Common functions
121 +extern void ebpf_global_labels(netdata_syscall_stat_t *is,
122 + netdata_publish_syscall_t *pio,
123 + char **dim,
124 + char **name,
125 + int end);
126 +
127 +extern void ebpf_write_chart_cmd(char *type
128 + , char *id
129 + , char *axis
130 + , char *web
131 + , int order);
132 +
133 +extern void ebpf_write_global_dimension(char *n, char *d);
134
127 -# define NETDATA_KEY_CALLS_VFS_READ 5
128 -# define NETDATA_KEY_ERROR_VFS_READ 6
129 -# define NETDATA_KEY_BYTES_VFS_READ 7
135 +extern void ebpf_create_global_dimension(void *ptr, int end);
136
131 -# define NETDATA_KEY_CALLS_VFS_UNLINK 8
132 -# define NETDATA_KEY_ERROR_VFS_UNLINK 9
137 +extern void ebpf_create_chart(char *family
138 + , char *name
139 + , char *axis
140 + , char *web
141 + , int order
142 + , void (*ncd)(void *, int)
143 + , void *move
144 + , int end);
145
134 -# define NETDATA_KEY_CALLS_DO_EXIT 10
146 +extern void write_begin_chart(char *family, char *name);
147
136 -# define NETDATA_KEY_CALLS_RELEASE_TASK 11
148 +extern void write_chart_dimension(char *dim, long long value);
149
138 -# define NETDATA_KEY_CALLS_DO_FORK 12
139 -# define NETDATA_KEY_ERROR_DO_FORK 13
150 +extern void write_count_chart(char *name, char *family, netdata_publish_syscall_t *move, int end);
151
141 -# define NETDATA_KEY_CALLS_CLOSE_FD 14
142 -# define NETDATA_KEY_ERROR_CLOSE_FD 15
152 +extern void write_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end);
153
144 -# define NETDATA_KEY_CALLS_SYS_CLONE 16
145 -# define NETDATA_KEY_ERROR_SYS_CLONE 17
154 +void write_io_chart(char *chart, char *family, char *dwrite, char *dread, netdata_publish_vfs_common_t *pvc);
155
147 -# define NETDATA_KEY_CALLS_VFS_WRITEV 18
148 -# define NETDATA_KEY_ERROR_VFS_WRITEV 19
149 -# define NETDATA_KEY_BYTES_VFS_WRITEV 20
156 +extern void fill_ebpf_functions(ebpf_functions_t *ef);
157
151 -# define NETDATA_KEY_CALLS_VFS_READV 21
152 -# define NETDATA_KEY_ERROR_VFS_READV 22
153 -# define NETDATA_KEY_BYTES_VFS_READV 23
158 +# define EBPF_GLOBAL_SECTION "global"
159 +# define EBPF_PROGRAMS_SECTION "ebpf programs"
160
161 #endif
collectors/ebpf.plugin/ebpf_process.c new
+514
@@ -0,0 +1,514 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include <sys/resource.h>
4 +
5 +#include "ebpf.h"
6 +#include "ebpf_process.h"
7 +
8 +/*****************************************************************
9 + *
10 + * GLOBAL VARIABLES
11 + *
12 + *****************************************************************/
13 +
14 +static char *process_dimension_names[NETDATA_MAX_MONITOR_VECTOR] = { "open", "close", "delete", "read", "write",
15 + "process", "task", "process", "thread" };
16 +static char *process_id_names[NETDATA_MAX_MONITOR_VECTOR] = { "do_sys_open", "__close_fd", "vfs_unlink", "vfs_read", "vfs_write",
17 + "do_exit", "release_task", "_do_fork", "sys_clone" };
18 +static char *status[] = { "process", "zombie" };
19 +
20 +static netdata_idx_t *process_hash_values = NULL;
21 +static netdata_syscall_stat_t *process_aggregated_data = NULL;
22 +static netdata_publish_syscall_t *process_publish_aggregated = NULL;
23 +
24 +static ebpf_functions_t process_functions;
25 +
26 +#ifndef STATIC
27 +/**
28 + * Pointers used when collector is dynamically linked
29 + */
30 +
31 +//Libbpf (It is necessary to have at least kernel 4.10)
32 +static int (*bpf_map_lookup_elem)(int, const void *, void *);
33 +
34 +static int *map_fd = NULL;
35 +/**
36 + * End of the pointers
37 + */
38 + #endif
39 +
40 +/*****************************************************************
41 + *
42 + * PROCESS DATA AND SEND TO NETDATA
43 + *
44 + *****************************************************************/
45 +
46 +/**
47 + * Update publish structure before to send data to Netdata.
48 + *
49 + * @param publish the first output structure with independent dimensions
50 + * @param pvc the second output structure with correlated dimensions
51 + * @param input the structure with the input data.
52 + */
53 +static void ebpf_update_publish(netdata_publish_syscall_t *publish,
54 + netdata_publish_vfs_common_t *pvc,
55 + netdata_syscall_stat_t *input) {
56 +
57 + netdata_publish_syscall_t *move = publish;
58 + while(move) {
59 + if(input->call != move->pcall) {
60 + //This condition happens to avoid initial values with dimensions higher than normal values.
61 + if(move->pcall) {
62 + move->ncall = (input->call > move->pcall)?input->call - move->pcall: move->pcall - input->call;
63 + move->nbyte = (input->bytes > move->pbyte)?input->bytes - move->pbyte: move->pbyte - input->bytes;
64 + move->nerr = (input->ecall > move->nerr)?input->ecall - move->perr: move->perr - input->ecall;
65 + } else {
66 + move->ncall = 0;
67 + move->nbyte = 0;
68 + move->nerr = 0;
69 + }
70 +
71 + move->pcall = input->call;
72 + move->pbyte = input->bytes;
73 + move->perr = input->ecall;
74 + } else {
75 + move->ncall = 0;
76 + move->nbyte = 0;
77 + move->nerr = 0;
78 + }
79 +
80 + input = input->next;
81 + move = move->next;
82 + }
83 +
84 + pvc->write = -((long)publish[2].nbyte);
85 + pvc->read = (long)publish[3].nbyte;
86 +
87 + pvc->running = (long)publish[7].ncall - (long)publish[8].ncall;
88 + publish[6].ncall = -publish[6].ncall; // release
89 + pvc->zombie = (long)publish[5].ncall + (long)publish[6].ncall;
90 +}
91 +
92 +
93 +/**
94 + * Call the necessary functions to create a chart.
95 + *
96 + * @param family the chart family
97 + * @param move the pointer with the values that will be published
98 + */
99 +static void write_status_chart(char *family, netdata_publish_vfs_common_t *pvc) {
100 + write_begin_chart(family, NETDATA_PROCESS_STATUS_NAME);
101 +
102 + write_chart_dimension(status[0], (long long) pvc->running);
103 + write_chart_dimension(status[1], (long long) pvc->zombie);
104 +
105 + printf("END\n");
106 +}
107 +
108 +/**
109 + * Send data to Netdata calling auxiliar functions.
110 + *
111 + * @param em the structure with thread information
112 + */
113 +static void ebpf_process_send_data(ebpf_module_t *em) {
114 + netdata_publish_vfs_common_t pvc;
115 + ebpf_update_publish(process_publish_aggregated, &pvc, process_aggregated_data);
116 +
117 + write_count_chart(NETDATA_FILE_OPEN_CLOSE_COUNT, NETDATA_EBPF_FAMILY, process_publish_aggregated, 2);
118 + write_count_chart(NETDATA_VFS_FILE_CLEAN_COUNT,
119 + NETDATA_EBPF_FAMILY,
120 + &process_publish_aggregated[NETDATA_DEL_START],
121 + 1);
122 + write_count_chart(NETDATA_VFS_FILE_IO_COUNT,
123 + NETDATA_EBPF_FAMILY,
124 + &process_publish_aggregated[NETDATA_IN_START_BYTE],
125 + 2);
126 + write_count_chart(NETDATA_EXIT_SYSCALL,
127 + NETDATA_EBPF_FAMILY,
128 + &process_publish_aggregated[NETDATA_EXIT_START],
129 + 2);
130 + write_count_chart(NETDATA_PROCESS_SYSCALL,
131 + NETDATA_EBPF_FAMILY,
132 + &process_publish_aggregated[NETDATA_PROCESS_START],
133 + 2);
134 +
135 + write_status_chart(NETDATA_EBPF_FAMILY, &pvc);
136 + if(em->mode < MODE_ENTRY) {
137 + write_err_chart(NETDATA_FILE_OPEN_ERR_COUNT, NETDATA_EBPF_FAMILY, process_publish_aggregated, 2);
138 + write_err_chart(NETDATA_VFS_FILE_ERR_COUNT,
139 + NETDATA_EBPF_FAMILY,
140 + &process_publish_aggregated[2],
141 + NETDATA_VFS_ERRORS);
142 + write_err_chart(NETDATA_PROCESS_ERROR_NAME,
143 + NETDATA_EBPF_FAMILY,
144 + &process_publish_aggregated[NETDATA_PROCESS_START],
145 + 2);
146 +
147 + write_io_chart(NETDATA_VFS_IO_FILE_BYTES, NETDATA_EBPF_FAMILY, process_id_names[3],
148 + process_id_names[4], &pvc);
149 + }
150 +}
151 +
152 +/*****************************************************************
153 + *
154 + * READ INFORMATION FROM KERNEL RING
155 + *
156 + *****************************************************************/
157 +
158 +/**
159 + * Read the hash table and store data to allocated vectors.
160 + */
161 +static void read_hash_global_tables()
162 +{
163 + uint64_t idx;
164 + netdata_idx_t res[NETDATA_GLOBAL_VECTOR];
165 +
166 + netdata_idx_t *val = process_hash_values;
167 + for (idx = 0; idx < NETDATA_GLOBAL_VECTOR; idx++) {
168 + if(!bpf_map_lookup_elem(map_fd[1], &idx, val)) {
169 + uint64_t total = 0;
170 + int i;
171 + int end = (running_on_kernel < NETDATA_KERNEL_V4_15)?1:ebpf_nprocs;
172 + for (i = 0; i < end; i++)
173 + total += val[i];
174 +
175 + res[idx] = total;
176 + } else {
177 + res[idx] = 0;
178 + }
179 + }
180 +
181 + process_aggregated_data[0].call = res[NETDATA_KEY_CALLS_DO_SYS_OPEN];
182 + process_aggregated_data[1].call = res[NETDATA_KEY_CALLS_CLOSE_FD];
183 + process_aggregated_data[2].call = res[NETDATA_KEY_CALLS_VFS_UNLINK];
184 + process_aggregated_data[3].call = res[NETDATA_KEY_CALLS_VFS_READ] + res[NETDATA_KEY_CALLS_VFS_READV];
185 + process_aggregated_data[4].call = res[NETDATA_KEY_CALLS_VFS_WRITE] + res[NETDATA_KEY_CALLS_VFS_WRITEV];
186 + process_aggregated_data[5].call = res[NETDATA_KEY_CALLS_DO_EXIT];
187 + process_aggregated_data[6].call = res[NETDATA_KEY_CALLS_RELEASE_TASK];
188 + process_aggregated_data[7].call = res[NETDATA_KEY_CALLS_DO_FORK];
189 + process_aggregated_data[8].call = res[NETDATA_KEY_CALLS_SYS_CLONE];
190 +
191 + process_aggregated_data[0].ecall = res[NETDATA_KEY_ERROR_DO_SYS_OPEN];
192 + process_aggregated_data[1].ecall = res[NETDATA_KEY_ERROR_CLOSE_FD];
193 + process_aggregated_data[2].ecall = res[NETDATA_KEY_ERROR_VFS_UNLINK];
194 + process_aggregated_data[3].ecall = res[NETDATA_KEY_ERROR_VFS_READ] + res[NETDATA_KEY_ERROR_VFS_READV];
195 + process_aggregated_data[4].ecall = res[NETDATA_KEY_ERROR_VFS_WRITE] + res[NETDATA_KEY_ERROR_VFS_WRITEV];
196 + process_aggregated_data[7].ecall = res[NETDATA_KEY_ERROR_DO_FORK];
197 + process_aggregated_data[8].ecall = res[NETDATA_KEY_ERROR_SYS_CLONE];
198 +
199 + process_aggregated_data[2].bytes = (uint64_t)res[NETDATA_KEY_BYTES_VFS_WRITE] +
200 + (uint64_t)res[NETDATA_KEY_BYTES_VFS_WRITEV];
201 + process_aggregated_data[3].bytes = (uint64_t)res[NETDATA_KEY_BYTES_VFS_READ] +
202 + (uint64_t)res[NETDATA_KEY_BYTES_VFS_READV];
203 +}
204 +
205 +/*****************************************************************
206 + *
207 + * FUNCTIONS WITH THE MAIN LOOP
208 + *
209 + *****************************************************************/
210 +
211 +
212 +/**
213 + * Main loop for this collector.
214 + *
215 + * @param step the number of microseconds used with heart beat
216 + * @param em the structure with thread information
217 + */
218 +static void process_collector(usec_t step, ebpf_module_t *em)
219 +{
220 + heartbeat_t hb;
221 + heartbeat_init(&hb);
222 + while(!close_ebpf_plugin) {
223 + usec_t dt = heartbeat_next(&hb, step);
224 + (void)dt;
225 +
226 + read_hash_global_tables();
227 +
228 + pthread_mutex_lock(&lock);
229 + ebpf_process_send_data(em);
230 + pthread_mutex_unlock(&lock);
231 +
232 + fflush(stdout);
233 + }
234 +}
235 +
236 +/*****************************************************************
237 + *
238 + * FUNCTIONS TO CREATE CHARTS
239 + *
240 + *****************************************************************/
241 +
242 +/**
243 + * Create IO chart
244 + *
245 + * @param family the chart family
246 + * @param name the chart name
247 + * @param axis the axis label
248 + * @param web the group name used to attach the chart on dashaboard
249 + * @param order the order number of the specified chart
250 + */
251 +static void ebpf_create_io_chart(char *family, char *name, char *axis, char *web, int order) {
252 + printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
253 + , family
254 + , name
255 + , axis
256 + , web
257 + , order);
258 +
259 + printf("DIMENSION %s %s absolute 1 1\n", process_id_names[3], NETDATA_VFS_DIM_OUT_FILE_BYTES);
260 + printf("DIMENSION %s %s absolute 1 1\n", process_id_names[4], NETDATA_VFS_DIM_IN_FILE_BYTES);
261 +}
262 +
263 +/**
264 + * Create process status chart
265 + *
266 + * @param family the chart family
267 + * @param name the chart name
268 + * @param axis the axis label
269 + * @param web the group name used to attach the chart on dashaboard
270 + * @param order the order number of the specified chart
271 + */
272 +static void ebpf_process_status_chart(char *family, char *name, char *axis, char *web, int order) {
273 + printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
274 + , family
275 + , name
276 + , axis
277 + , web
278 + , order);
279 +
280 + printf("DIMENSION %s '' absolute 1 1\n", status[0]);
281 + printf("DIMENSION %s '' absolute 1 1\n", status[1]);
282 +}
283 +
284 +/**
285 + * Create global charts
286 + *
287 + * Call ebpf_create_chart to create the charts for the collector.
288 + *
289 + * @param em a pointer to the structure with the default values.
290 + */
291 +static void ebpf_create_global_charts(ebpf_module_t *em) {
292 + ebpf_create_chart(NETDATA_EBPF_FAMILY
293 + , NETDATA_FILE_OPEN_CLOSE_COUNT
294 + , "Calls"
295 + , NETDATA_FILE_GROUP
296 + , 970
297 + , ebpf_create_global_dimension
298 + , process_publish_aggregated
299 + , 2);
300 +
301 + if (em->mode < MODE_ENTRY) {
302 + ebpf_create_chart(NETDATA_EBPF_FAMILY
303 + , NETDATA_FILE_OPEN_ERR_COUNT
304 + , "Calls"
305 + , NETDATA_FILE_GROUP
306 + , 971
307 + , ebpf_create_global_dimension
308 + , process_publish_aggregated
309 + , 2);
310 + }
311 +
312 + ebpf_create_chart(NETDATA_EBPF_FAMILY
313 + , NETDATA_VFS_FILE_CLEAN_COUNT
314 + , "Calls"
315 + , NETDATA_VFS_GROUP
316 + , 972
317 + , ebpf_create_global_dimension
318 + , &process_publish_aggregated[NETDATA_DEL_START]
319 + , 1);
320 +
321 + ebpf_create_chart(NETDATA_EBPF_FAMILY
322 + , NETDATA_VFS_FILE_IO_COUNT
323 + , "Calls"
324 + , NETDATA_VFS_GROUP
325 + , 973
326 + , ebpf_create_global_dimension
327 + , &process_publish_aggregated[NETDATA_IN_START_BYTE]
328 + , 2);
329 +
330 + if (em->mode < MODE_ENTRY) {
331 + ebpf_create_io_chart(NETDATA_EBPF_FAMILY
332 + , NETDATA_VFS_IO_FILE_BYTES
333 + , "bytes/s"
334 + , NETDATA_VFS_GROUP
335 + , 974);
336 +
337 + ebpf_create_chart(NETDATA_EBPF_FAMILY
338 + , NETDATA_VFS_FILE_ERR_COUNT
339 + , "Calls"
340 + , NETDATA_VFS_GROUP
341 + , 975
342 + , ebpf_create_global_dimension
343 + , &process_publish_aggregated[2]
344 + , NETDATA_VFS_ERRORS);
345 +
346 + }
347 +
348 + ebpf_create_chart(NETDATA_EBPF_FAMILY
349 + , NETDATA_PROCESS_SYSCALL
350 + , "Calls"
351 + , NETDATA_PROCESS_GROUP
352 + , 976
353 + , ebpf_create_global_dimension
354 + , &process_publish_aggregated[NETDATA_PROCESS_START]
355 + , 2);
356 +
357 + ebpf_create_chart(NETDATA_EBPF_FAMILY
358 + , NETDATA_EXIT_SYSCALL
359 + , "Calls"
360 + , NETDATA_PROCESS_GROUP
361 + , 977
362 + , ebpf_create_global_dimension
363 + , &process_publish_aggregated[NETDATA_EXIT_START]
364 + , 2);
365 +
366 + ebpf_process_status_chart(NETDATA_EBPF_FAMILY
367 + , NETDATA_PROCESS_STATUS_NAME
368 + , "Total"
369 + , NETDATA_PROCESS_GROUP
370 + , 978);
371 +
372 + if (em->mode < MODE_ENTRY) {
373 + ebpf_create_chart(NETDATA_EBPF_FAMILY
374 + , NETDATA_PROCESS_ERROR_NAME
375 + , "Calls"
376 + , NETDATA_PROCESS_GROUP
377 + , 979
378 + , ebpf_create_global_dimension
379 + , &process_publish_aggregated[NETDATA_PROCESS_START]
380 + , 2);
381 + }
382 +
383 +}
384 +
385 +/*****************************************************************
386 + *
387 + * FUNCTIONS TO CLOSE THE THREAD
388 + *
389 + *****************************************************************/
390 +
391 +/**
392 + * Clean up the main thread.
393 + *
394 + * @param ptr thread data.
395 + */
396 +static void ebpf_process_cleanup(void *ptr)
397 +{
398 + (void)ptr;
399 + freez(process_aggregated_data);
400 + freez(process_publish_aggregated);
401 + freez(process_hash_values);
402 +
403 + if (process_functions.libnetdata) {
404 + dlclose(process_functions.libnetdata);
405 + }
406 +
407 + freez(process_functions.map_fd);
408 +}
409 +
410 +/*****************************************************************
411 + *
412 + * FUNCTIONS TO START THREAD
413 + *
414 + *****************************************************************/
415 +
416 +/**
417 + * Allocate vectors used with this thread.
418 + * We are not testing the return, because callocz does this and shutdown the software
419 + * case it was not possible to allocate.
420 + *
421 + * @param length is the length for the vectors used inside the collector.
422 + */
423 +static void ebpf_process_allocate_global_vectors(size_t length) {
424 + process_aggregated_data = callocz(length, sizeof(netdata_syscall_stat_t));
425 + process_publish_aggregated = callocz(length, sizeof(netdata_publish_syscall_t));
426 + process_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
427 +}
428 +
429 +static void change_collector_event() {
430 + int i;
431 + if (running_on_kernel < NETDATA_KERNEL_V5_3)
432 + process_probes[10].name = NULL;
433 +
434 + for (i = 0; process_probes[i].name ; i++ ) {
435 + process_probes[i].type = 'p';
436 + }
437 +}
438 +
439 +static void change_syscalls() {
440 + static char *lfork = { "do_fork" };
441 + process_id_names[7] = lfork;
442 + process_probes[8].name = lfork;
443 +}
444 +
445 +/**
446 + * Set local function pointers, this function will never be compiled with static libraries
447 + */
448 +static void set_local_pointers(ebpf_module_t *em) {
449 +#ifndef STATIC
450 + bpf_map_lookup_elem = process_functions.bpf_map_lookup_elem;
451 +
452 +#endif
453 +
454 + map_fd = process_functions.map_fd;
455 +
456 + if (em->mode == MODE_ENTRY) {
457 + change_collector_event();
458 + }
459 +
460 + if (process_functions.isrh >= NETDATA_MINIMUM_RH_VERSION && process_functions.isrh < NETDATA_RH_8)
461 + change_syscalls();
462 +}
463 +
464 +/*****************************************************************
465 + *
466 + * EBPF PROCESS THREAD
467 + *
468 + *****************************************************************/
469 +
470 +/**
471 + * Process thread
472 + *
473 + * Thread used to generate process charts.
474 + *
475 + * @param ptr a pointer to `struct ebpf_module`
476 + *
477 + * @return It always return NULL
478 + */
479 +void *ebpf_process_thread(void *ptr)
480 +{
481 + netdata_thread_cleanup_push(ebpf_process_cleanup, ptr);
482 +
483 + ebpf_module_t *em = (ebpf_module_t *)ptr;
484 + fill_ebpf_functions(&process_functions);
485 +
486 + if (!em->enabled)
487 + goto endprocess;
488 +
489 + pthread_mutex_lock(&lock);
490 + ebpf_process_allocate_global_vectors(NETDATA_MAX_MONITOR_VECTOR);
491 +
492 + if (ebpf_load_libraries(&process_functions, "libnetdata_ebpf.so", ebpf_plugin_dir)) {
493 + pthread_mutex_unlock(&lock);
494 + goto endprocess;
495 + }
496 +
497 + set_local_pointers(em);
498 + if (ebpf_load_program(ebpf_plugin_dir, em->thread_id, em->mode, kernel_string,
499 + em->thread_name, process_functions.map_fd, process_functions.load_bpf_file) ) {
500 + pthread_mutex_unlock(&lock);
501 + goto endprocess;
502 + }
503 +
504 + ebpf_global_labels(process_aggregated_data, process_publish_aggregated, process_dimension_names,
505 + process_id_names, NETDATA_MAX_MONITOR_VECTOR);
506 +
507 + ebpf_create_global_charts(em);
508 + pthread_mutex_unlock(&lock);
509 + process_collector((usec_t)(em->update_time*USEC_PER_SEC), em);
510 +
511 +endprocess:
512 + netdata_thread_cleanup_pop(1);
513 + return NULL;
514 +}
collectors/ebpf.plugin/ebpf_process.h new
+71
@@ -0,0 +1,71 @@
1 +#ifndef _NETDATA_EBPF_PROCESS_H_
2 +# define _NETDATA_EBPF_PROCESS_H_ 1
3 +
4 +# define NETDATA_FILE_GROUP "File"
5 +# define NETDATA_VFS_GROUP "VFS"
6 +# define NETDATA_PROCESS_GROUP "Process"
7 +
8 +# define NETDATA_GLOBAL_VECTOR 24
9 +# define NETDATA_MAX_MONITOR_VECTOR 9
10 +# define NETDATA_VFS_ERRORS 3
11 +
12 +# define NETDATA_DEL_START 2
13 +# define NETDATA_IN_START_BYTE 3
14 +# define NETDATA_EXIT_START 5
15 +# define NETDATA_PROCESS_START 7
16 +
17 +# define NETDATA_FILE_OPEN_CLOSE_COUNT "file_descriptor"
18 +# define NETDATA_FILE_OPEN_ERR_COUNT "file_error"
19 +# define NETDATA_VFS_FILE_CLEAN_COUNT "deleted_objects"
20 +# define NETDATA_VFS_FILE_IO_COUNT "io"
21 +# define NETDATA_VFS_FILE_ERR_COUNT "io_error"
22 +
23 +# define NETDATA_EXIT_SYSCALL "exit"
24 +# define NETDATA_PROCESS_SYSCALL "process_thread"
25 +# define NETDATA_PROCESS_ERROR_NAME "task_error"
26 +# define NETDATA_PROCESS_STATUS_NAME "process_status"
27 +
28 +# define NETDATA_VFS_IO_FILE_BYTES "io_bytes"
29 +# define NETDATA_VFS_DIM_IN_FILE_BYTES "write"
30 +# define NETDATA_VFS_DIM_OUT_FILE_BYTES "read"
31 +
32 +//Index from kernel
33 +typedef enum ebpf_process_index {
34 + NETDATA_KEY_CALLS_DO_SYS_OPEN,
35 + NETDATA_KEY_ERROR_DO_SYS_OPEN,
36 +
37 + NETDATA_KEY_CALLS_VFS_WRITE,
38 + NETDATA_KEY_ERROR_VFS_WRITE,
39 + NETDATA_KEY_BYTES_VFS_WRITE,
40 +
41 + NETDATA_KEY_CALLS_VFS_READ,
42 + NETDATA_KEY_ERROR_VFS_READ,
43 + NETDATA_KEY_BYTES_VFS_READ,
44 +
45 + NETDATA_KEY_CALLS_VFS_UNLINK,
46 + NETDATA_KEY_ERROR_VFS_UNLINK,
47 +
48 + NETDATA_KEY_CALLS_DO_EXIT,
49 +
50 + NETDATA_KEY_CALLS_RELEASE_TASK,
51 +
52 + NETDATA_KEY_CALLS_DO_FORK,
53 + NETDATA_KEY_ERROR_DO_FORK,
54 +
55 + NETDATA_KEY_CALLS_CLOSE_FD,
56 + NETDATA_KEY_ERROR_CLOSE_FD,
57 +
58 + NETDATA_KEY_CALLS_SYS_CLONE,
59 + NETDATA_KEY_ERROR_SYS_CLONE,
60 +
61 + NETDATA_KEY_CALLS_VFS_WRITEV,
62 + NETDATA_KEY_ERROR_VFS_WRITEV,
63 + NETDATA_KEY_BYTES_VFS_WRITEV,
64 +
65 + NETDATA_KEY_CALLS_VFS_READV,
66 + NETDATA_KEY_ERROR_VFS_READV,
67 + NETDATA_KEY_BYTES_VFS_READV
68 +
69 +} ebpf_process_index_t;
70 +
71 +# endif
collectors/ebpf.plugin/ebpf_socket.c new
+387
@@ -0,0 +1,387 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include <sys/resource.h>
4 +
5 +#include "ebpf.h"
6 +#include "ebpf_socket.h"
7 +
8 +/*****************************************************************
9 + *
10 + * GLOBAL VARIABLES
11 + *
12 + *****************************************************************/
13 +
14 +static ebpf_functions_t socket_functions;
15 +
16 +static netdata_idx_t *socket_hash_values = NULL;
17 +static netdata_syscall_stat_t *socket_aggregated_data = NULL;
18 +static netdata_publish_syscall_t *socket_publish_aggregated = NULL;
19 +
20 +static char *socket_dimension_names[NETDATA_MAX_SOCKET_VECTOR] = { "sent", "received", "close", "sent", "received" };
21 +static char *socket_id_names[NETDATA_MAX_SOCKET_VECTOR] = { "tcp_sendmsg", "tcp_cleanup_rbuf", "tcp_close", "udp_sendmsg",
22 + "udp_recvmsg" };
23 +
24 +#ifndef STATIC
25 +/**
26 + * Pointers used when collector is dynamically linked
27 + */
28 +
29 +//Libbpf (It is necessary to have at least kernel 4.10)
30 +static int (*bpf_map_lookup_elem)(int, const void *, void *);
31 +static int (*bpf_map_delete_elem)(int fd, const void *key);
32 +
33 +static int *map_fd = NULL;
34 +/**
35 + * End of the pointers
36 + */
37 +#endif
38 +
39 +/*****************************************************************
40 + *
41 + * PROCESS DATA AND SEND TO NETDATA
42 + *
43 + *****************************************************************/
44 +
45 +/**
46 + * Update publish structure before to send data to Netdata.
47 + *
48 + * @param publish the first output structure with independent dimensions
49 + * @param tcp structure to store IO from tcp sockets
50 + * @param udp structure to store IO from udp sockets
51 + * @param input the structure with the input data.
52 + */
53 +static void ebpf_update_publish(netdata_publish_syscall_t *publish,
54 + netdata_publish_vfs_common_t *tcp,
55 + netdata_publish_vfs_common_t *udp,
56 + netdata_syscall_stat_t *input) {
57 +
58 + netdata_publish_syscall_t *move = publish;
59 + while(move) {
60 + if(input->call != move->pcall) {
61 + //This condition happens to avoid initial values with dimensions higher than normal values.
62 + if(move->pcall) {
63 + move->ncall = (input->call > move->pcall)?input->call - move->pcall: move->pcall - input->call;
64 + move->nbyte = (input->bytes > move->pbyte)?input->bytes - move->pbyte: move->pbyte - input->bytes;
65 + move->nerr = (input->ecall > move->nerr)?input->ecall - move->perr: move->perr - input->ecall;
66 + } else {
67 + move->ncall = 0;
68 + move->nbyte = 0;
69 + move->nerr = 0;
70 + }
71 +
72 + move->pcall = input->call;
73 + move->pbyte = input->bytes;
74 + move->perr = input->ecall;
75 + } else {
76 + move->ncall = 0;
77 + move->nbyte = 0;
78 + move->nerr = 0;
79 + }
80 +
81 + input = input->next;
82 + move = move->next;
83 + }
84 +
85 + tcp->write = -((long)publish[0].nbyte);
86 + tcp->read = (long)publish[1].nbyte;
87 +
88 + udp->write = -((long)publish[3].nbyte);
89 + udp->read = (long)publish[4].nbyte;
90 +}
91 +
92 +/**
93 + * Send data to Netdata calling auxiliar functions.
94 + *
95 + * @param em the structure with thread information
96 + */
97 +static void ebpf_process_send_data(ebpf_module_t *em) {
98 + netdata_publish_vfs_common_t common_tcp;
99 + netdata_publish_vfs_common_t common_udp;
100 + ebpf_update_publish(socket_publish_aggregated, &common_tcp, &common_udp, socket_aggregated_data);
101 +
102 + write_count_chart(NETDATA_TCP_FUNCTION_COUNT, NETDATA_EBPF_FAMILY, socket_publish_aggregated, 3);
103 + write_io_chart(NETDATA_TCP_FUNCTION_BYTES, NETDATA_EBPF_FAMILY, socket_id_names[0], socket_id_names[1], &common_tcp);
104 + if (em->mode < MODE_ENTRY) {
105 + write_err_chart(NETDATA_TCP_FUNCTION_ERROR, NETDATA_EBPF_FAMILY, socket_publish_aggregated, 2);
106 + }
107 +
108 + write_count_chart(NETDATA_UDP_FUNCTION_COUNT, NETDATA_EBPF_FAMILY,
109 + &socket_publish_aggregated[NETDATA_UDP_START], 2);
110 + write_io_chart(NETDATA_UDP_FUNCTION_BYTES, NETDATA_EBPF_FAMILY, socket_id_names[3], socket_id_names[4], &common_udp);
111 + if (em->mode < MODE_ENTRY) {
112 + write_err_chart(NETDATA_UDP_FUNCTION_ERROR, NETDATA_EBPF_FAMILY,
113 + &socket_publish_aggregated[NETDATA_UDP_START], 2);
114 + }
115 +}
116 +
117 +/*****************************************************************
118 + *
119 + * FUNCTIONS TO CREATE CHARTS
120 + *
121 + *****************************************************************/
122 +
123 +/**
124 + * Create global charts
125 + *
126 + * Call ebpf_create_chart to create the charts for the collector.
127 + *
128 + * @param em a pointer to the structure with the default values.
129 + */
130 +static void ebpf_create_global_charts(ebpf_module_t *em) {
131 + ebpf_create_chart(NETDATA_EBPF_FAMILY
132 + , NETDATA_TCP_FUNCTION_COUNT
133 + , "Calls"
134 + , NETDATA_SOCKET_GROUP
135 + , 950
136 + , ebpf_create_global_dimension
137 + , socket_publish_aggregated
138 + , 3);
139 +
140 + ebpf_create_chart(NETDATA_EBPF_FAMILY
141 + , NETDATA_TCP_FUNCTION_BYTES
142 + , "bytes/s"
143 + , NETDATA_SOCKET_GROUP
144 + , 951
145 + , ebpf_create_global_dimension
146 + , socket_publish_aggregated
147 + , 3);
148 +
149 + if (em->mode < MODE_ENTRY) {
150 + ebpf_create_chart(NETDATA_EBPF_FAMILY
151 + , NETDATA_TCP_FUNCTION_ERROR
152 + , "Calls"
153 + , NETDATA_SOCKET_GROUP
154 + , 952
155 + , ebpf_create_global_dimension
156 + , socket_publish_aggregated
157 + , 2);
158 + }
159 +
160 + ebpf_create_chart(NETDATA_EBPF_FAMILY
161 + , NETDATA_UDP_FUNCTION_COUNT
162 + , "Calls"
163 + , NETDATA_SOCKET_GROUP
164 + , 953
165 + , ebpf_create_global_dimension
166 + , &socket_publish_aggregated[NETDATA_UDP_START]
167 + , 2);
168 +
169 + ebpf_create_chart(NETDATA_EBPF_FAMILY
170 + , NETDATA_UDP_FUNCTION_BYTES
171 + , "bytes/s"
172 + , NETDATA_SOCKET_GROUP
173 + , 954
174 + , ebpf_create_global_dimension
175 + , &socket_publish_aggregated[NETDATA_UDP_START]
176 + , 2);
177 +
178 + if (em->mode < MODE_ENTRY) {
179 + ebpf_create_chart(NETDATA_EBPF_FAMILY
180 + , NETDATA_UDP_FUNCTION_ERROR
181 + , "Calls"
182 + , NETDATA_SOCKET_GROUP
183 + , 955
184 + , ebpf_create_global_dimension
185 + , &socket_publish_aggregated[NETDATA_UDP_START]
186 + , 2);
187 + }
188 +}
189 +
190 +/*****************************************************************
191 + *
192 + * READ INFORMATION FROM KERNEL RING
193 + *
194 + *****************************************************************/
195 +
196 +/**
197 + * Read the hash table and store data to allocated vectors.
198 + */
199 +static void read_hash_global_tables()
200 +{
201 + uint64_t idx;
202 + netdata_idx_t res[NETDATA_SOCKET_COUNTER];
203 +
204 + netdata_idx_t *val = socket_hash_values;
205 + for (idx = 0; idx < NETDATA_SOCKET_COUNTER ; idx++) {
206 + if (!bpf_map_lookup_elem(map_fd[4], &idx, val)) {
207 + uint64_t total = 0;
208 + int i;
209 + int end = (running_on_kernel < NETDATA_KERNEL_V4_15) ? 1 : ebpf_nprocs;
210 + for (i = 0; i < end; i++)
211 + total += val[i];
212 +
213 + res[idx] = total;
214 + } else {
215 + res[idx] = 0;
216 + }
217 + }
218 +
219 + socket_aggregated_data[0].call = res[NETDATA_KEY_CALLS_TCP_SENDMSG];
220 + socket_aggregated_data[1].call = res[NETDATA_KEY_CALLS_TCP_CLEANUP_RBUF];
221 + socket_aggregated_data[2].call = res[NETDATA_KEY_CALLS_TCP_CLOSE];
222 + socket_aggregated_data[3].call = res[NETDATA_KEY_CALLS_UDP_RECVMSG];
223 + socket_aggregated_data[4].call = res[NETDATA_KEY_CALLS_UDP_SENDMSG];
224 +
225 + socket_aggregated_data[0].ecall = res[NETDATA_KEY_ERROR_TCP_SENDMSG];
226 + socket_aggregated_data[1].ecall = res[NETDATA_KEY_ERROR_TCP_CLEANUP_RBUF];
227 + socket_aggregated_data[3].ecall = res[NETDATA_KEY_ERROR_UDP_RECVMSG];
228 + socket_aggregated_data[4].ecall = res[NETDATA_KEY_ERROR_UDP_SENDMSG];
229 +
230 + socket_aggregated_data[0].bytes = res[NETDATA_KEY_BYTES_TCP_SENDMSG];
231 + socket_aggregated_data[1].bytes = res[NETDATA_KEY_BYTES_TCP_CLEANUP_RBUF];
232 + socket_aggregated_data[3].bytes = res[NETDATA_KEY_BYTES_UDP_RECVMSG];
233 + socket_aggregated_data[4].bytes = res[NETDATA_KEY_BYTES_UDP_SENDMSG];
234 +}
235 +
236 +/*****************************************************************
237 + *
238 + * FUNCTIONS WITH THE MAIN LOOP
239 + *
240 + *****************************************************************/
241 +
242 +
243 +/**
244 + * Main loop for this collector.
245 + *
246 + * @param step the number of microseconds used with heart beat
247 + * @param em the structure with thread information
248 + */
249 +static void socket_collector(usec_t step, ebpf_module_t *em)
250 +{
251 + (void)em;
252 + heartbeat_t hb;
253 + heartbeat_init(&hb);
254 + while(!close_ebpf_plugin) {
255 + usec_t dt = heartbeat_next(&hb, step);
256 + (void)dt;
257 +
258 + read_hash_global_tables();
259 +
260 + pthread_mutex_lock(&lock);
261 + ebpf_process_send_data(em);
262 + pthread_mutex_unlock(&lock);
263 +
264 + fflush(stdout);
265 + }
266 +}
267 +
268 +/*****************************************************************
269 + *
270 + * FUNCTIONS TO CLOSE THE THREAD
271 + *
272 + *****************************************************************/
273 +
274 +/**
275 + * Clean up the main thread.
276 + *
277 + * @param ptr thread data.
278 + */
279 +static void ebpf_socket_cleanup(void *ptr)
280 +{
281 + (void)ptr;
282 +
283 + freez(socket_aggregated_data);
284 + freez(socket_publish_aggregated);
285 + freez(socket_hash_values);
286 +
287 + if (socket_functions.libnetdata) {
288 + dlclose(socket_functions.libnetdata);
289 + }
290 +
291 + freez(socket_functions.map_fd);
292 +}
293 +
294 +/*****************************************************************
295 + *
296 + * FUNCTIONS TO START THREAD
297 + *
298 + *****************************************************************/
299 +
300 +/**
301 + * Allocate vectors used with this thread.
302 + * We are not testing the return, because callocz does this and shutdown the software
303 + * case it was not possible to allocate.
304 + *
305 + * @param length is the length for the vectors used inside the collector.
306 + */
307 +static void ebpf_socket_allocate_global_vectors(size_t length) {
308 + socket_aggregated_data = callocz(length, sizeof(netdata_syscall_stat_t));
309 + socket_publish_aggregated = callocz(length, sizeof(netdata_publish_syscall_t));
310 + socket_hash_values = callocz(ebpf_nprocs, sizeof(netdata_idx_t));
311 +}
312 +
313 +static void change_collector_event() {
314 + socket_probes[0].type = 'p';
315 + socket_probes[5].type = 'p';
316 +}
317 +
318 +/**
319 + * Set local function pointers, this function will never be compiled with static libraries
320 + */
321 +static void set_local_pointers(ebpf_module_t *em) {
322 +#ifndef STATIC
323 + bpf_map_lookup_elem = socket_functions.bpf_map_lookup_elem;
324 + (void) bpf_map_lookup_elem;
325 + bpf_map_delete_elem = socket_functions.bpf_map_delete_elem;
326 + (void) bpf_map_delete_elem;
327 +#endif
328 + map_fd = socket_functions.map_fd;
329 +
330 + if (em->mode == MODE_ENTRY) {
331 + change_collector_event();
332 + }
333 +}
334 +
335 +/*****************************************************************
336 + *
337 + * EBPF SOCKET THREAD
338 + *
339 + *****************************************************************/
340 +
341 +/**
342 + * Socket thread
343 + *
344 + * Thread used to generate socket charts.
345 + *
346 + * @param ptr a pointer to `struct ebpf_module`
347 + *
348 + * @return It always return NULL
349 + */
350 +void *ebpf_socket_thread(void *ptr)
351 +{
352 + netdata_thread_cleanup_push(ebpf_socket_cleanup, ptr);
353 +
354 + ebpf_module_t *em = (ebpf_module_t *)ptr;
355 + fill_ebpf_functions(&socket_functions);
356 +
357 + if (!em->enabled)
358 + goto endsocket;
359 +
360 + pthread_mutex_lock(&lock);
361 +
362 + ebpf_socket_allocate_global_vectors(NETDATA_MAX_SOCKET_VECTOR);
363 +
364 + if (ebpf_load_libraries(&socket_functions, "libnetdata_ebpf.so", ebpf_plugin_dir)) {
365 + pthread_mutex_unlock(&lock);
366 + goto endsocket;
367 + }
368 +
369 + set_local_pointers(em);
370 + if (ebpf_load_program(ebpf_plugin_dir, em->thread_id, em->mode, kernel_string,
371 + em->thread_name, socket_functions.map_fd, socket_functions.load_bpf_file) ) {
372 + pthread_mutex_unlock(&lock);
373 + goto endsocket;
374 + }
375 +
376 + ebpf_global_labels(socket_aggregated_data, socket_publish_aggregated, socket_dimension_names,
377 + socket_id_names, NETDATA_MAX_SOCKET_VECTOR);
378 +
379 + ebpf_create_global_charts(em);
380 + pthread_mutex_unlock(&lock);
381 +
382 + socket_collector((usec_t)(em->update_time*USEC_PER_SEC), em);
383 +
384 +endsocket:
385 + netdata_thread_cleanup_pop(1);
386 + return NULL;
387 +}
collectors/ebpf.plugin/ebpf_socket.h new
+40
@@ -0,0 +1,40 @@
1 +#ifndef _NETDATA_EBPF_SOCKET_H_
2 +# define _NETDATA_EBPF_SOCKET_H_ 1
3 +
4 +# define NETDATA_SOCKET_COUNTER 13
5 +
6 +# define NETDATA_MAX_SOCKET_VECTOR 5
7 +
8 +# define NETDATA_UDP_START 3
9 +
10 +typedef enum ebpf_socket_idx {
11 + NETDATA_KEY_CALLS_TCP_SENDMSG,
12 + NETDATA_KEY_ERROR_TCP_SENDMSG,
13 + NETDATA_KEY_BYTES_TCP_SENDMSG,
14 +
15 + NETDATA_KEY_CALLS_TCP_CLEANUP_RBUF,
16 + NETDATA_KEY_ERROR_TCP_CLEANUP_RBUF,
17 + NETDATA_KEY_BYTES_TCP_CLEANUP_RBUF,
18 +
19 + NETDATA_KEY_CALLS_TCP_CLOSE,
20 +
21 + NETDATA_KEY_CALLS_UDP_RECVMSG,
22 + NETDATA_KEY_ERROR_UDP_RECVMSG,
23 + NETDATA_KEY_BYTES_UDP_RECVMSG,
24 +
25 + NETDATA_KEY_CALLS_UDP_SENDMSG,
26 + NETDATA_KEY_ERROR_UDP_SENDMSG,
27 + NETDATA_KEY_BYTES_UDP_SENDMSG
28 +} ebpf_socket_index_t;
29 +
30 +# define NETDATA_SOCKET_GROUP "Socket"
31 +
32 +# define NETDATA_TCP_FUNCTION_COUNT "tcp_functions"
33 +# define NETDATA_TCP_FUNCTION_BYTES "tcp_bandwidth"
34 +# define NETDATA_TCP_FUNCTION_ERROR "tcp_error"
35 +# define NETDATA_UDP_FUNCTION_COUNT "udp_functions"
36 +# define NETDATA_UDP_FUNCTION_BYTES "udp_bandwidth"
37 +# define NETDATA_UDP_FUNCTION_ERROR "udp_error"
38 +
39 +
40 +#endif
libnetdata/ebpf/ebpf.c
+98 -2
@@ -1,6 +1,7 @@
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>
4 +#include <dlfcn.h>
5
6 #include "../libnetdata.h"
7
@@ -14,7 +15,7 @@ static int clean_kprobe_event(FILE *out, char *filename, char *father_pid, netda
15 }
16
17 char cmd[1024];
17 - int length = sprintf(cmd, "-:kprobes/%c_netdata_%s_%s", ptr->type, ptr->name, father_pid);
18 + int length = snprintf(cmd, 1023, "-:kprobes/%c_netdata_%s_%s", ptr->type, ptr->name, father_pid);
19 int ret = 0;
20 if (length > 0) {
21 ssize_t written = write(fd, cmd, strlen(cmd));
@@ -91,7 +92,7 @@ int get_kernel_version(char *out, int size) {
92
93 fd = snprintf(out, (size_t)size, "%s.%s.%s", major, minor, patch);
94 if (fd > size)
94 - error("[EBPF]: The buffer to store kernel version is not smaller than necessary.");
95 + error("The buffer to store kernel version is not smaller than necessary.");
96
97 return ((int)(str2l(major)*65536) + (int)(str2l(minor)*256) + (int)str2l(patch));
98 }
@@ -168,3 +169,98 @@ char *ebpf_library_suffix(int version, int isrh) {
169
170 return NULL;
171 }
172 +
173 +//----------------------------------------------------------------------------------------------------------------------
174 +
175 +int ebpf_load_libraries(ebpf_functions_t *ef, char *libbase, char *pluginsdir)
176 +{
177 + char *err = NULL;
178 + char lpath[4096];
179 + char netdatasl[128];
180 + void *libnetdata;
181 +
182 + snprintf(netdatasl, 127, "%s.%s", libbase, ef->kernel_string);
183 + snprintf(lpath, 4095, "%s/%s", pluginsdir, netdatasl);
184 + libnetdata = dlopen(lpath, RTLD_LAZY);
185 + if (!libnetdata) {
186 + info("Cannot load library %s for the current kernel.", lpath);
187 +
188 + //Update kernel
189 + char *library = ebpf_library_suffix(ef->running_on_kernel, (ef->isrh < 0)?0:1);
190 + size_t length = strlen(library);
191 + strncpyz(ef->kernel_string, library, length);
192 + ef->kernel_string[length] = '\0';
193 +
194 + //Try to load the default version
195 + snprintf(netdatasl, 127, "%s.%s", libbase, ef->kernel_string);
196 + snprintf(lpath, 4095, "%s/%s", pluginsdir, netdatasl);
197 + libnetdata = dlopen(lpath, RTLD_LAZY);
198 + if (!libnetdata) {
199 + error("Cannot load %s default library.", lpath);
200 + return -1;
201 + } else {
202 + info("Default shared library %s loaded with success.", lpath);
203 + ef->libnetdata = libnetdata;
204 + }
205 + } else {
206 + info("Current shared library %s loaded with success.", lpath);
207 + ef->libnetdata = libnetdata;
208 + }
209 +
210 + ef->load_bpf_file = dlsym(libnetdata, "load_bpf_file");
211 + if ((err = dlerror()) != NULL) {
212 + error("Cannot find load_bpf_file: %s", err);
213 + return -1;
214 + }
215 +
216 + ef->bpf_map_lookup_elem = dlsym(libnetdata, "bpf_map_lookup_elem");
217 + if ((err = dlerror()) != NULL) {
218 + error("Cannot find bpf_map_lookup_elem: %s", err);
219 + return -1;
220 + }
221 +
222 + ef->bpf_map_delete_elem = dlsym(libnetdata, "bpf_map_delete_elem");
223 + if ((err = dlerror()) != NULL) {
224 + error("Cannot find bpf_map_delete_elem: %s", err);
225 + return -1;
226 + }
227 +
228 + return 0;
229 +}
230 +
231 +static int select_file(char *name, const char *program, size_t length, int mode , char *kernel_string) {
232 + int ret = -1;
233 + if (!mode)
234 + ret = snprintf(name, length, "rnetdata_ebpf_%s.%s.o", program, kernel_string);
235 + else if(mode == 1)
236 + ret = snprintf(name, length, "dnetdata_ebpf_%s.%s.o", program, kernel_string);
237 + else if(mode == 2)
238 + ret = snprintf(name, length, "pnetdata_ebpf_%s.%s.o", program, kernel_string);
239 +
240 + return ret;
241 +}
242 +
243 +int ebpf_load_program(char *plugins_dir,
244 + int event_id, int mode ,
245 + char *kernel_string,
246 + const char *name,
247 + int *map_fd,
248 + int (*load_bpf_file)(int *, char *, int))
249 +{
250 + char lpath[4096];
251 + char lname[128];
252 +
253 + int test = select_file(lname, name, (size_t)127, mode, kernel_string);
254 + if (test < 0 || test > 127)
255 + return -1;
256 +
257 + snprintf(lpath, 4096, "%s/%s", plugins_dir, lname);
258 + if (load_bpf_file(map_fd, lpath, event_id)) {
259 + error("Cannot load program: %s", lpath);
260 + return -1;
261 + } else {
262 + info("The eBPF program %s was loaded with success.", name);
263 + }
264 +
265 + return 0;
266 +}
libnetdata/ebpf/ebpf.h
+21 -1
@@ -50,13 +50,33 @@
50 typedef struct netdata_ebpf_events {
51 char type;
52 char *name;
53 -
53 } netdata_ebpf_events_t;
54
55 +typedef struct ebpf_functions {
56 + void *libnetdata;
57 + int (*load_bpf_file)(int *, char *, int);
58 + //Libbpf (It is necessary to have at least kernel 4.10)
59 + int (*bpf_map_lookup_elem)(int, const void *, void *);
60 + int (*bpf_map_delete_elem)(int fd, const void *key);
61 +
62 + int *map_fd;
63 +
64 + char *kernel_string;
65 + uint32_t running_on_kernel;
66 + int isrh;
67 +} ebpf_functions_t;
68 +
69 extern int clean_kprobe_events(FILE *out, int pid, netdata_ebpf_events_t *ptr);
70 extern int get_kernel_version(char *out, int size);
71 extern int get_redhat_release();
72 extern int has_condition_to_run(int version);
73 extern char *ebpf_library_suffix(int version, int isrh);
74 +extern int ebpf_load_libraries(ebpf_functions_t *ef, char *libbase, char *pluginsdir);
75 +extern int ebpf_load_program(char *plugins_dir,
76 + int event_id, int mode,
77 + char *kernel_string,
78 + const char *name,
79 + int *map_fd,
80 + int (*load_bpf_file)(int *,char *, int));
81
82 #endif