summaryrefslogtreecommitdiffstats
path: root/collectors/ebpf.plugin/ebpf.c
diff options
context:
space:
mode:
Diffstat (limited to 'collectors/ebpf.plugin/ebpf.c')
-rw-r--r--collectors/ebpf.plugin/ebpf.c1152
1 files changed, 1152 insertions, 0 deletions
diff --git a/collectors/ebpf.plugin/ebpf.c b/collectors/ebpf.plugin/ebpf.c
new file mode 100644
index 0000000000..b3fa7f71bf
--- /dev/null
+++ b/collectors/ebpf.plugin/ebpf.c
@@ -0,0 +1,1152 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "ebpf.h"
+
+// callback required by eval()
+int health_variable_lookup(const char *variable, uint32_t hash, struct rrdcalc *rc, calculated_number *result) {
+ (void)variable;
+ (void)hash;
+ (void)rc;
+ (void)result;
+ return 0;
+};
+
+void send_statistics( const char *action, const char *action_result, const char *action_data) {
+ (void) action;
+ (void) action_result;
+ (void) action_data;
+ return;
+}
+
+// callbacks required by popen()
+void signals_block(void) {};
+void signals_unblock(void) {};
+void signals_reset(void) {};
+
+// required by get_system_cpus()
+char *netdata_configured_host_prefix = "";
+
+// callback required by fatal()
+void netdata_cleanup_and_exit(int ret) {
+ exit(ret);
+}
+
+// ----------------------------------------------------------------------
+//Netdata eBPF library
+void *libnetdata = NULL;
+int (*load_bpf_file)(char *, int) = NULL;
+int (*set_bpf_perf_event)(int, int);
+int (*perf_event_unmap)(struct perf_event_mmap_page *, size_t);
+int (*perf_event_mmap_header)(int, struct perf_event_mmap_page **, int);
+void (*netdata_perf_loop_multi)(int *, struct perf_event_mmap_page **, int, int *, int (*nsb)(void *, int), int);
+int *map_fd = NULL;
+
+//Perf event variables
+static int pmu_fd[NETDATA_MAX_PROCESSOR];
+static struct perf_event_mmap_page *headers[NETDATA_MAX_PROCESSOR];
+int page_cnt = 8;
+
+//Libbpf (It is necessary to have at least kernel 4.10)
+int (*bpf_map_lookup_elem)(int, const void *, void *);
+
+static char *plugin_dir = PLUGINS_DIR;
+static char *user_config_dir = CONFIG_DIR;
+static char *stock_config_dir = LIBCONFIG_DIR;
+static char *netdata_configured_log_dir = LOG_DIR;
+
+FILE *developer_log = NULL;
+
+//Global vectors
+netdata_syscall_stat_t *aggregated_data = NULL;
+netdata_publish_syscall_t *publish_aggregated = NULL;
+
+static int update_every = 1;
+static int thread_finished = 0;
+static int close_plugin = 0;
+static netdata_run_mode_t mode = MODE_ENTRY;
+static int debug_log = 0;
+static int use_stdout = 0;
+struct config collector_config;
+static int mykernel = 0;
+static int nprocs;
+static int isrh;
+netdata_idx_t *hash_values;
+
+pthread_mutex_t lock;
+
+static struct ebpf_module {
+ const char *thread_name;
+ int enabled;
+ void (*start_routine) (void *);
+ int update_time;
+ int global_charts;
+ int apps_charts;
+ netdata_run_mode_t mode;
+} ebpf_modules[] = {
+ { .thread_name = "process", .enabled = 0, .start_routine = NULL, .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY },
+ { .thread_name = "network_viewer", .enabled = 0, .start_routine = NULL, .update_time = 1, .global_charts = 1, .apps_charts = 1, .mode = MODE_ENTRY },
+ { .thread_name = NULL, .enabled = 0, .start_routine = NULL, .update_time = 1, .global_charts = 0, .apps_charts = 1, .mode = MODE_ENTRY },
+};
+
+static char *dimension_names[NETDATA_MAX_MONITOR_VECTOR] = { "open", "close", "delete", "read", "write", "process", "task", "process", "thread" };
+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" };
+static char *status[] = { "process", "zombie" };
+
+int event_pid = 0;
+netdata_ebpf_events_t collector_events[] = {
+ { .type = 'r', .name = "vfs_write" },
+ { .type = 'r', .name = "vfs_writev" },
+ { .type = 'r', .name = "vfs_read" },
+ { .type = 'r', .name = "vfs_readv" },
+ { .type = 'r', .name = "do_sys_open" },
+ { .type = 'r', .name = "vfs_unlink" },
+ { .type = 'p', .name = "do_exit" },
+ { .type = 'p', .name = "release_task" },
+ { .type = 'r', .name = "_do_fork" },
+ { .type = 'r', .name = "__close_fd" },
+ { .type = 'r', .name = "__x64_sys_clone" },
+ { .type = 0, .name = NULL }
+};
+
+void open_developer_log() {
+ char filename[FILENAME_MAX+1];
+ int tot = sprintf(filename, "%s/%s", netdata_configured_log_dir, NETDATA_DEVELOPER_LOG_FILE);
+
+ if(tot > 0)
+ developer_log = fopen(filename, "a");
+}
+
+static int unmap_memory() {
+ int i;
+ int size = (int)sysconf(_SC_PAGESIZE)*(page_cnt + 1);
+ for ( i = 0 ; i < nprocs ; i++ ) {
+ if (perf_event_unmap(headers[i], size) < 0) {
+ fprintf(stderr,"[EBPF PROCESS] CANNOT unmap headers.\n");
+ return -1;
+ }
+
+ close(pmu_fd[i]);
+ }
+
+ return 0;
+}
+
+static void int_exit(int sig)
+{
+ close_plugin = 1;
+
+ //When both threads were not finished case I try to go in front this address, the collector will crash
+ if (!thread_finished) {
+ return;
+ }
+
+ if (aggregated_data) {
+ free(aggregated_data);
+ aggregated_data = NULL;
+ }
+
+ if (publish_aggregated) {
+ free(publish_aggregated);
+ publish_aggregated = NULL;
+ }
+
+ if(mode == MODE_DEVMODE && debug_log) {
+ unmap_memory();
+ }
+
+ if (libnetdata) {
+ dlclose(libnetdata);
+ libnetdata = NULL;
+ }
+
+ if (developer_log) {
+ fclose(developer_log);
+ developer_log = NULL;
+ }
+
+ if (hash_values) {
+ freez(hash_values);
+ }
+
+ if (event_pid) {
+ int ret = fork();
+ if (ret < 0) //error
+ error("[EBPF PROCESS] Cannot fork(), so I won't be able to clean %skprobe_events", NETDATA_DEBUGFS);
+ else if (!ret) { //child
+ int i;
+ for ( i=getdtablesize(); i>=0; --i)
+ close(i);
+
+ int fd = open("/dev/null",O_RDWR, 0);
+ if (fd != -1) {
+ dup2 (fd, STDIN_FILENO);
+ dup2 (fd, STDOUT_FILENO);
+ dup2 (fd, STDERR_FILENO);
+ }
+
+ if (fd > 2)
+ close (fd);
+
+ int sid = setsid();
+ if(sid >= 0) {
+ sleep(1);
+ if(debug_log) {
+ open_developer_log();
+ }
+ debug(D_EXIT, "Wait for father %d die", event_pid);
+ clean_kprobe_events(developer_log, event_pid, collector_events);
+ } else {
+ error("Cannot become session id leader, so I won't try to clean kprobe_events.\n");
+ }
+ } else { //parent
+ exit(0);
+ }
+
+ if (developer_log) {
+ fclose(developer_log);
+ developer_log = NULL;
+ }
+ }
+
+ exit(sig);
+}
+
+static inline void netdata_write_chart_cmd(char *type
+ , char *id
+ , char *axis
+ , char *web
+ , int order)
+{
+ printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
+ , type
+ , id
+ , axis
+ , web
+ , order);
+}
+
+static void netdata_write_global_dimension(char *d, char *n)
+{
+ printf("DIMENSION %s %s absolute 1 1\n", d, n);
+}
+
+static void netdata_create_global_dimension(void *ptr, int end)
+{
+ netdata_publish_syscall_t *move = ptr;
+
+ int i = 0;
+ while (move && i < end) {
+ netdata_write_global_dimension(move->name, move->dimension);
+
+ move = move->next;
+ i++;
+ }
+}
+static inline void netdata_create_chart(char *family
+ , char *name
+ , char *axis
+ , char *web
+ , int order
+ , void (*ncd)(void *, int)
+ , void *move
+ , int end)
+{
+ netdata_write_chart_cmd(family, name, axis, web, order);
+
+ ncd(move, end);
+}
+
+static void netdata_create_io_chart(char *family, char *name, char *axis, char *web, int order) {
+ printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
+ , family
+ , name
+ , axis
+ , web
+ , order);
+
+ printf("DIMENSION %s %s absolute 1 1\n", id_names[3], NETDATA_VFS_DIM_OUT_FILE_BYTES);
+ printf("DIMENSION %s %s absolute 1 1\n", id_names[4], NETDATA_VFS_DIM_IN_FILE_BYTES);
+}
+
+static void netdata_process_status_chart(char *family, char *name, char *axis, char *web, int order) {
+ printf("CHART %s.%s '' '' '%s' '%s' '' line %d 1 ''\n"
+ , family
+ , name
+ , axis
+ , web
+ , order);
+
+ printf("DIMENSION %s '' absolute 1 1\n", status[0]);
+ printf("DIMENSION %s '' absolute 1 1\n", status[1]);
+}
+
+static void netdata_global_charts_create() {
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_FILE_OPEN_CLOSE_COUNT
+ , "Calls"
+ , NETDATA_FILE_GROUP
+ , 970
+ , netdata_create_global_dimension
+ , publish_aggregated
+ , 2);
+
+ if(mode < MODE_ENTRY) {
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_FILE_OPEN_ERR_COUNT
+ , "Calls"
+ , NETDATA_FILE_GROUP
+ , 971
+ , netdata_create_global_dimension
+ , publish_aggregated
+ , 2);
+ }
+
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_VFS_FILE_CLEAN_COUNT
+ , "Calls"
+ , NETDATA_VFS_GROUP
+ , 972
+ , netdata_create_global_dimension
+ , &publish_aggregated[NETDATA_DEL_START]
+ , 1);
+
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_VFS_FILE_IO_COUNT
+ , "Calls"
+ , NETDATA_VFS_GROUP
+ , 973
+ , netdata_create_global_dimension
+ , &publish_aggregated[NETDATA_IN_START_BYTE]
+ , 2);
+
+ if(mode < MODE_ENTRY) {
+ netdata_create_io_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_VFS_IO_FILE_BYTES
+ , "bytes/s"
+ , NETDATA_VFS_GROUP
+ , 974);
+
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_VFS_FILE_ERR_COUNT
+ , "Calls"
+ , NETDATA_VFS_GROUP
+ , 975
+ , netdata_create_global_dimension
+ , &publish_aggregated[2]
+ , NETDATA_VFS_ERRORS);
+
+ }
+
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_PROCESS_SYSCALL
+ , "Calls"
+ , NETDATA_PROCESS_GROUP
+ , 976
+ , netdata_create_global_dimension
+ , &publish_aggregated[NETDATA_PROCESS_START]
+ , 2);
+
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_EXIT_SYSCALL
+ , "Calls"
+ , NETDATA_PROCESS_GROUP
+ , 977
+ , netdata_create_global_dimension
+ , &publish_aggregated[NETDATA_EXIT_START]
+ , 2);
+
+ netdata_process_status_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_PROCESS_STATUS_NAME
+ , "Total"
+ , NETDATA_PROCESS_GROUP
+ , 978);
+
+ if(mode < MODE_ENTRY) {
+ netdata_create_chart(NETDATA_EBPF_FAMILY
+ , NETDATA_PROCESS_ERROR_NAME
+ , "Calls"
+ , NETDATA_PROCESS_GROUP
+ , 979
+ , netdata_create_global_dimension
+ , &publish_aggregated[NETDATA_PROCESS_START]
+ , 2);
+ }
+
+}
+
+
+static void netdata_create_charts() {
+ netdata_global_charts_create();
+}
+
+static void netdata_update_publish(netdata_publish_syscall_t *publish
+ , netdata_publish_vfs_common_t *pvc
+ , netdata_syscall_stat_t *input) {
+
+ netdata_publish_syscall_t *move = publish;
+ while(move) {
+ if(input->call != move->pcall) {
+ //This condition happens to avoid initial values with dimensions higher than normal values.
+ if(move->pcall) {
+ move->ncall = (input->call > move->pcall)?input->call - move->pcall: move->pcall - input->call;
+ move->nbyte = (input->bytes > move->pbyte)?input->bytes - move->pbyte: move->pbyte - input->bytes;
+ move->nerr = (input->ecall > move->nerr)?input->ecall - move->perr: move->perr - input->ecall;
+ } else {
+ move->ncall = 0;
+ move->nbyte = 0;
+ move->nerr = 0;
+ }
+
+ move->pcall = input->call;
+ move->pbyte = input->bytes;
+ move->perr = input->ecall;
+ } else {
+ move->ncall = 0;
+ move->nbyte = 0;
+ move->nerr = 0;
+ }
+
+ input = input->next;
+ move = move->next;
+ }
+
+ pvc->write = -((long)publish[2].nbyte);
+ pvc->read = (long)publish[3].nbyte;
+
+ pvc->running = (long)publish[7].ncall - (long)publish[8].ncall;
+ publish[6].ncall = -publish[6].ncall; // release
+ pvc->zombie = (long)publish[5].ncall + (long)publish[6].ncall;
+}
+
+static inline void write_begin_chart(char *family, char *name)
+{
+ int ret = printf( "BEGIN %s.%s\n"
+ , family
+ , name);
+
+ (void)ret;
+}
+
+static inline void write_chart_dimension(char *dim, long long value)
+{
+ int ret = printf("SET %s = %lld\n", dim, value);
+ (void)ret;
+}
+
+static void write_global_count_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
+ write_begin_chart(family, name);
+
+ int i = 0;
+ while (move && i < end) {
+ write_chart_dimension(move->name, move->ncall);
+
+ move = move->next;
+ i++;
+ }
+
+ printf("END\n");
+}
+
+static void write_global_err_chart(char *name, char *family, netdata_publish_syscall_t *move, int end) {
+ write_begin_chart(family, name);
+
+ int i = 0;
+ while (move && i < end) {
+ write_chart_dimension(move->name, move->nerr);
+
+ move = move->next;
+ i++;
+ }
+
+ printf("END\n");
+}
+
+static void write_io_chart(char *family, netdata_publish_vfs_common_t *pvc) {
+ write_begin_chart(family, NETDATA_VFS_IO_FILE_BYTES);
+
+ write_chart_dimension(id_names[3], (long long) pvc->write);
+ write_chart_dimension(id_names[4], (long long) pvc->read);
+
+ printf("END\n");
+}
+
+static void write_status_chart(char *family, netdata_publish_vfs_common_t *pvc) {
+ write_begin_chart(family, NETDATA_PROCESS_STATUS_NAME);
+
+ write_chart_dimension(status[0], (long long) pvc->running);
+ write_chart_dimension(status[1], (long long) pvc->zombie);
+
+ printf("END\n");
+}
+
+static void netdata_publish_data() {
+ netdata_publish_vfs_common_t pvc;
+ netdata_update_publish(publish_aggregated, &pvc, aggregated_data);
+
+ write_global_count_chart(NETDATA_FILE_OPEN_CLOSE_COUNT, NETDATA_EBPF_FAMILY, publish_aggregated, 2);
+ write_global_count_chart(NETDATA_VFS_FILE_CLEAN_COUNT, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_DEL_START], 1);
+ write_global_count_chart(NETDATA_VFS_FILE_IO_COUNT, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_IN_START_BYTE], 2);
+ write_global_count_chart(NETDATA_EXIT_SYSCALL, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_EXIT_START], 2);
+ write_global_count_chart(NETDATA_PROCESS_SYSCALL, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_PROCESS_START], 2);
+
+ write_status_chart(NETDATA_EBPF_FAMILY, &pvc);
+ if(mode < MODE_ENTRY) {
+ write_global_err_chart(NETDATA_FILE_OPEN_ERR_COUNT, NETDATA_EBPF_FAMILY, publish_aggregated, 2);
+ write_global_err_chart(NETDATA_VFS_FILE_ERR_COUNT, NETDATA_EBPF_FAMILY, &publish_aggregated[2], NETDATA_VFS_ERRORS);
+ write_global_err_chart(NETDATA_PROCESS_ERROR_NAME, NETDATA_EBPF_FAMILY, &publish_aggregated[NETDATA_PROCESS_START], 2);
+
+ write_io_chart(NETDATA_EBPF_FAMILY, &pvc);
+ }
+}
+
+void *process_publisher(void *ptr)
+{
+ (void)ptr;
+ netdata_create_charts();
+
+ usec_t step = update_every * USEC_PER_SEC;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ while(!close_plugin) {
+ usec_t dt = heartbeat_next(&hb, step);
+ (void)dt;
+
+ pthread_mutex_lock(&lock);
+ netdata_publish_data();
+ pthread_mutex_unlock(&lock);
+
+ fflush(stdout);
+ }
+
+ return NULL;
+}
+
+static void move_from_kernel2user_global() {
+ uint64_t idx;
+ netdata_idx_t res[NETDATA_GLOBAL_VECTOR];
+
+ netdata_idx_t *val = hash_values;
+ for (idx = 0; idx < NETDATA_GLOBAL_VECTOR; idx++) {
+ if(!bpf_map_lookup_elem(map_fd[1], &idx, val)) {
+ uint64_t total = 0;
+ int i;
+ int end = (mykernel < NETDATA_KERNEL_V4_15)?1:nprocs;
+ for (i = 0; i < end; i++)
+ total += val[i];
+
+ res[idx] = total;
+ } else {
+ res[idx] = 0;
+ }
+ }
+
+ aggregated_data[0].call = res[NETDATA_KEY_CALLS_DO_SYS_OPEN];
+ aggregated_data[1].call = res[NETDATA_KEY_CALLS_CLOSE_FD];
+ aggregated_data[2].call = res[NETDATA_KEY_CALLS_VFS_UNLINK];
+ aggregated_data[3].call = res[NETDATA_KEY_CALLS_VFS_READ] + res[NETDATA_KEY_CALLS_VFS_READV];
+ aggregated_data[4].call = res[NETDATA_KEY_CALLS_VFS_WRITE] + res[NETDATA_KEY_CALLS_VFS_WRITEV];
+ aggregated_data[5].call = res[NETDATA_KEY_CALLS_DO_EXIT];
+ aggregated_data[6].call = res[NETDATA_KEY_CALLS_RELEASE_TASK];
+ aggregated_data[7].call = res[NETDATA_KEY_CALLS_DO_FORK];
+ aggregated_data[8].call = res[NETDATA_KEY_CALLS_SYS_CLONE];
+
+ aggregated_data[0].ecall = res[NETDATA_KEY_ERROR_DO_SYS_OPEN];
+ aggregated_data[1].ecall = res[NETDATA_KEY_ERROR_CLOSE_FD];
+ aggregated_data[2].ecall = res[NETDATA_KEY_ERROR_VFS_UNLINK];
+ aggregated_data[3].ecall = res[NETDATA_KEY_ERROR_VFS_READ] + res[NETDATA_KEY_ERROR_VFS_READV];
+ aggregated_data[4].ecall = res[NETDATA_KEY_ERROR_VFS_WRITE] + res[NETDATA_KEY_ERROR_VFS_WRITEV];
+ aggregated_data[7].ecall = res[NETDATA_KEY_ERROR_DO_FORK];
+ aggregated_data[8].ecall = res[NETDATA_KEY_ERROR_SYS_CLONE];
+
+ aggregated_data[2].bytes = (uint64_t)res[NETDATA_KEY_BYTES_VFS_WRITE] + (uint64_t)res[NETDATA_KEY_BYTES_VFS_WRITEV];
+ aggregated_data[3].bytes = (uint64_t)res[NETDATA_KEY_BYTES_VFS_READ] + (uint64_t)res[NETDATA_KEY_BYTES_VFS_READV];
+}
+
+static void move_from_kernel2user()
+{
+ move_from_kernel2user_global();
+}
+
+void *process_collector(void *ptr)
+{
+ (void)ptr;
+
+ usec_t step = 778879ULL;
+ heartbeat_t hb;
+ heartbeat_init(&hb);
+ while(!close_plugin) {
+ usec_t dt = heartbeat_next(&hb, step);
+ (void)dt;
+
+ pthread_mutex_lock(&lock);
+ move_from_kernel2user();
+ pthread_mutex_unlock(&lock);
+ }
+
+ return NULL;
+}
+
+static int netdata_store_bpf(void *data, int size) {
+ (void)size;
+
+ if (close_plugin)
+ return 0;
+
+ if(!debug_log)
+ return -2; //LIBBPF_PERF_EVENT_CONT;
+
+ netdata_error_report_t *e = data;
+ fprintf(developer_log
+ ,"%llu %s %u: %s, %d\n"
+ , now_realtime_usec() ,e->comm, e->pid, dimension_names[e->type], e->err);
+ fflush(developer_log);
+
+ return -2; //LIBBPF_PERF_EVENT_CONT;
+}
+
+void *process_log(void *ptr)
+{
+ (void) ptr;
+
+ if (mode == MODE_DEVMODE && debug_log) {
+ netdata_perf_loop_multi(pmu_fd, headers, nprocs, &close_plugin, netdata_store_bpf, page_cnt);
+ }
+
+ return NULL;
+}
+
+void set_global_labels() {
+ int i;
+
+ netdata_syscall_stat_t *is = aggregated_data;
+ netdata_syscall_stat_t *prev = NULL;
+
+ netdata_publish_syscall_t *pio = publish_aggregated;
+ netdata_publish_syscall_t *publish_prev = NULL;
+ for (i = 0; i < NETDATA_MAX_MONITOR_VECTOR; i++) {
+ if(prev) {
+ prev->next = &is[i];
+ }
+ prev = &is[i];
+
+ pio[i].dimension = dimension_names[i];
+ pio[i].name = id_names[i];
+ if(publish_prev) {
+ publish_prev->next = &pio[i];
+ }
+ publish_prev = &pio[i];
+ }
+}
+
+int allocate_global_vectors() {
+ aggregated_data = callocz(NETDATA_MAX_MONITOR_VECTOR, sizeof(netdata_syscall_stat_t));
+ if(!aggregated_data) {
+ return -1;
+ }
+
+ publish_aggregated = callocz(NETDATA_MAX_MONITOR_VECTOR, sizeof(netdata_publish_syscall_t));
+ if(!publish_aggregated) {
+ return -1;
+ }
+
+ hash_values = callocz(nprocs, sizeof(netdata_idx_t));
+ if(!hash_values) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void build_complete_path(char *out, size_t length,char *path, char *filename) {
+ if(path){
+ snprintf(out, length, "%s/%s", path, filename);
+ } else {
+ snprintf(out, length, "%s", filename);
+ }
+}
+
+static int map_memory() {
+ int i;
+ for (i = 0; i < nprocs; i++) {
+ pmu_fd[i] = set_bpf_perf_event(i, 2);
+
+ if (perf_event_mmap_header(pmu_fd[i], &headers[i], page_cnt) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int ebpf_load_libraries()
+{
+ char *err = NULL;
+ char lpath[4096];
+
+ build_complete_path(lpath, 4096, plugin_dir, "libnetdata_ebpf.so");
+ libnetdata = dlopen(lpath, RTLD_LAZY);
+ if (!libnetdata) {
+ error("[EBPF_PROCESS] Cannot load %s.", lpath);
+ return -1;
+ } else {
+ load_bpf_file = dlsym(libnetdata, "load_bpf_file");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find load_bpf_file: %s", err);
+ return -1;
+ }
+
+ map_fd = dlsym(libnetdata, "map_fd");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find map_fd: %s", err);
+ return -1;
+ }
+
+ bpf_map_lookup_elem = dlsym(libnetdata, "bpf_map_lookup_elem");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find bpf_map_lookup_elem: %s", err);
+ return -1;
+ }
+
+ if(mode == MODE_DEVMODE) {
+ set_bpf_perf_event = dlsym(libnetdata, "set_bpf_perf_event");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find set_bpf_perf_event: %s", err);
+ return -1;
+ }
+
+ perf_event_unmap = dlsym(libnetdata, "perf_event_unmap");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find perf_event_unmap: %s", err);
+ return -1;
+ }
+
+ perf_event_mmap_header = dlsym(libnetdata, "perf_event_mmap_header");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find perf_event_mmap_header: %s", err);
+ return -1;
+ }
+
+ netdata_perf_loop_multi = dlsym(libnetdata, "netdata_perf_loop_multi");
+ if ((err = dlerror()) != NULL) {
+ error("[EBPF_PROCESS] Cannot find netdata_perf_loop_multi: %s", err);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+char *select_file() {
+ if(!mode)
+ return "rnetdata_ebpf_process.o";
+ if(mode == MODE_DEVMODE)
+ return "dnetdata_ebpf_process.o";
+
+ return "pnetdata_ebpf_process.o";
+}
+
+int process_load_ebpf()
+{
+ char lpath[4096];
+
+ char *name = select_file();
+
+ build_complete_path(lpath, 4096, plugin_dir, name);
+ event_pid = getpid();
+ if (load_bpf_file(lpath, event_pid) ) {
+ error("[EBPF_PROCESS] Cannot load program: %s", lpath);
+ return -1;
+ } else {
+ info("[EBPF PROCESS]: The eBPF program %s was loaded with success.", name);
+ }
+
+ return 0;
+}
+
+void set_global_variables() {
+ //Get environment variables
+ plugin_dir = getenv("NETDATA_PLUGINS_DIR");
+ if(!plugin_dir)
+ plugin_dir = PLUGINS_DIR;
+
+ user_config_dir = getenv("NETDATA_USER_CONFIG_DIR");
+ if(!user_config_dir)
+ user_config_dir = CONFIG_DIR;
+
+ stock_config_dir = getenv("NETDATA_STOCK_CONFIG_DIR");
+ if(!stock_config_dir)
+ stock_config_dir = LIBCONFIG_DIR;
+
+ netdata_configured_log_dir = getenv("NETDATA_LOG_DIR");
+ if(!netdata_configured_log_dir)
+ netdata_configured_log_dir = LOG_DIR;
+
+ page_cnt *= (int)sysconf(_SC_NPROCESSORS_ONLN);
+
+ nprocs = (int)sysconf(_SC_NPROCESSORS_ONLN);
+ if (nprocs > NETDATA_MAX_PROCESSOR) {
+ nprocs = NETDATA_MAX_PROCESSOR;
+ }
+
+ isrh = get_redhat_release();
+}
+
+static void change_collector_event() {
+ int i;
+ if (mykernel < NETDATA_KERNEL_V5_3)
+ collector_events[10].name = NULL;
+
+ for (i = 0; collector_events[i].name ; i++ ) {
+ collector_events[i].type = 'p';
+ }
+}
+
+static void change_syscalls() {
+ static char *lfork = { "do_fork" };
+ id_names[7] = lfork;
+ collector_events[8].name = lfork;
+}
+
+static inline void what_to_load(char *ptr) {
+ if (!strcasecmp(ptr, "return"))
+ mode = MODE_RETURN;
+ /*
+ else if (!strcasecmp(ptr, "dev"))
+ mode = 1;
+ */
+ else
+ change_collector_event();
+
+ if (isrh >= NETDATA_MINIMUM_RH_VERSION && isrh < NETDATA_RH_8)
+ change_syscalls();
+}
+
+static inline void enable_debug(char *ptr) {
+ if (!strcasecmp(ptr, "yes"))
+ debug_log = 1;
+}
+
+static inline void set_log_file(char *ptr) {
+ if (!strcasecmp(ptr, "yes"))
+ use_stdout = 1;
+}
+
+static void set_global_values() {
+ struct section *sec = collector_config.first_section;
+ while(sec) {
+ if(!strcasecmp(sec->name, "global")) {
+ struct config_option *values = sec->values;
+ while(values) {
+ if(!strcasecmp(values->name, "load"))
+ what_to_load(values->value);
+ else if(!strcasecmp(values->name, "debug log"))
+ enable_debug(values->value);
+ else if(!strcasecmp(values->name, "use stdout"))
+ set_log_file(values->value);
+
+ values = values->next;
+ }
+ }
+ sec = sec->next;
+ }
+}
+
+static int load_collector_file(char *path) {
+ char lpath[4096];
+
+ build_complete_path(lpath, 4096, path, "ebpf.conf" );
+
+ if (!appconfig_load(&collector_config, lpath, 0, NULL))
+ return 1;
+
+ set_global_values();
+
+ return 0;
+}
+
+static inline void ebpf_disable_apps() {
+ int i ;
+ for (i = 0 ;ebpf_modules[i].thread_name ; i++ ) {
+ ebpf_modules[i].apps_charts = 0;
+ }
+}
+
+static inline void ebpf_enable_specific_chart(struct ebpf_module *em, int disable_apps) {
+ em->enabled = 1;
+ if (!disable_apps) {
+ em->apps_charts = 1;
+ }
+ em->global_charts = 1;
+}
+
+static inline void ebpf_enable_all_charts(int apps) {
+ int i ;
+ for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
+ ebpf_enable_specific_chart(&ebpf_modules[i], apps);
+ }
+}
+
+static inline void ebpf_enable_chart(int enable, int disable_apps) {
+ int i ;
+ for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
+ if (i == enable) {
+ ebpf_enable_specific_chart(&ebpf_modules[i], disable_apps);
+ break;
+ }
+ }
+}
+
+static inline void ebpf_set_thread_mode(netdata_run_mode_t lmode) {
+ int i ;
+ for (i = 0 ; ebpf_modules[i].thread_name ; i++ ) {
+ ebpf_modules[i].mode = lmode;
+ }
+}
+
+void ebpf_print_help() {
+ const time_t t = time(NULL);
+ struct tm ct;
+ struct tm *test = localtime_r(&t, &ct);
+ int year;
+ if (test)
+ year = ct.tm_year;
+ else
+ year = 0;
+
+ fprintf(stderr,
+ "\n"
+ " Netdata ebpf.plugin %s\n"
+ " Copyright (C) 2016-%d Costa Tsaousis <costa@tsaousis.gr>\n"
+ " Released under GNU General Public License v3 or later.\n"
+ " All rights reserved.\n"
+ "\n"
+ " This program is a data collector plugin for netdata.\n"
+ "\n"
+ " Available command line options:\n"
+ "\n"
+ " SECONDS set the data collection frequency.\n"
+ "\n"
+ " --help or -h show this help.\n"
+ "\n"
+ " --version or -v show software version.\n"
+ "\n"
+ " --global or -g disable charts per application.\n"
+ "\n"
+ " --all or -a Enable all chart groups (global and apps), unless -g is also given.\n"
+ "\n"
+ " --net or -n Enable network viewer charts.\n"
+ "\n"
+ " --process or -p Enable charts related to process run time.\n"
+ "\n"
+ " --return or -r Run the collector in return mode.\n"
+ "\n"
+ , VERSION
+ , (year >= 116)?year + 1900: 2020
+ );
+}
+
+static void parse_args(int argc, char **argv)
+{
+ int enabled = 0;
+ int disable_apps = 0;
+ int freq = 0;
+ int c;
+ int option_index = 0;
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h' },
+ {"version", no_argument, 0, 'v' },
+ {"global", no_argument, 0, 'g' },
+ {"all", no_argument, 0, 'a' },
+ {"net", no_argument, 0, 'n' },
+ {"process", no_argument, 0, 'p' },
+ {"return", no_argument, 0, 'r' },
+ {0, 0, 0, 0}
+ };
+
+ if (argc > 1) {
+ int n = (int)str2l(argv[1]);
+ if(n > 0) {
+ freq = n;
+ }
+ }
+
+ while (1) {
+ c = getopt_long(argc, argv, "hvganpr",long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h': {
+ ebpf_print_help();
+ exit(0);
+ }
+ case 'v': {
+ printf("ebpf.plugin %s\n", VERSION);
+ exit(0);
+ }
+ case 'g': {
+ disable_apps = 1;
+ ebpf_disable_apps();
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("EBPF running with global chart group, because it was started with the option \"--global\" or \"-g\".");
+#endif
+ break;
+ }
+ case 'a': {
+ ebpf_enable_all_charts(disable_apps);
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("EBPF running with all chart groups, because it was started with the option \"--all\" or \"-a\".");
+#endif
+ break;
+ }
+ case 'n': {
+ enabled = 1;
+ ebpf_enable_chart(1, disable_apps);
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("EBPF enabling \"NET\" charts, because it was started with the option \"--net\" or \"-n\".");
+#endif
+ break;
+ }
+ case 'p': {
+ enabled = 1;
+ ebpf_enable_chart(0, disable_apps);
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("EBPF enabling \"PROCESS\" charts, because it was started with the option \"--process\" or \"-p\".");
+#endif
+ break;
+ }
+ case 'r': {
+ mode = MODE_RETURN;
+ ebpf_set_thread_mode(mode);
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("EBPF running in \"return\" mode, because it was started with the option \"--return\" or \"-r\".");
+#endif
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+
+ if (freq > 0) {
+ update_every = freq;
+ }
+
+ if (!enabled) {
+ ebpf_enable_all_charts(disable_apps);
+#ifdef NETDATA_INTERNAL_CHECKS
+ info("EBPF running with all charts, because neither \"-n\" or \"-p\" was given.");
+#endif
+ }
+}
+
+int main(int argc, char **argv)
+{
+ parse_args(argc, argv);
+
+ mykernel = get_kernel_version();
+ if(!has_condition_to_run(mykernel)) {
+ error("[EBPF PROCESS] The current collector cannot run on this kernel.");
+ retu