diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-12-01 06:15:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-12-01 06:15:04 +0000 |
commit | e970e0b37b8bd7f246feb3f70c4136418225e434 (patch) | |
tree | 0b67c0ca45f56f2f9d9c5c2e725279ecdf52d2eb /libnetdata/ebpf | |
parent | Adding upstream version 1.31.0. (diff) | |
download | netdata-e970e0b37b8bd7f246feb3f70c4136418225e434.tar.xz netdata-e970e0b37b8bd7f246feb3f70c4136418225e434.zip |
Adding upstream version 1.32.0.upstream/1.32.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libnetdata/ebpf')
-rw-r--r-- | libnetdata/ebpf/ebpf.c | 392 | ||||
-rw-r--r-- | libnetdata/ebpf/ebpf.h | 76 |
2 files changed, 419 insertions, 49 deletions
diff --git a/libnetdata/ebpf/ebpf.c b/libnetdata/ebpf/ebpf.c index 1f71f6a24..1ccaa7b41 100644 --- a/libnetdata/ebpf/ebpf.c +++ b/libnetdata/ebpf/ebpf.c @@ -64,13 +64,19 @@ int clean_kprobe_events(FILE *out, int pid, netdata_ebpf_events_t *ptr) //---------------------------------------------------------------------------------------------------------------------- -int get_kernel_version(char *out, int size) +/** + * Get Kernel version + * + * Get the current kernel from /proc and returns an integer value representing it + * + * @return it returns a value representing the kernel version. + */ +int ebpf_get_kernel_version() { char major[16], minor[16], patch[16]; char ver[VERSION_STRING_LEN]; char *version = ver; - out[0] = '\0'; int fd = open("/proc/sys/kernel/osrelease", O_RDONLY); if (fd < 0) return -1; @@ -104,10 +110,6 @@ int get_kernel_version(char *out, int size) *move++ = *version++; *move = '\0'; - fd = snprintf(out, (size_t)size, "%s.%s.%s", major, minor, patch); - if (fd > size) - error("The buffer to store kernel version is not smaller than necessary."); - return ((int)(str2l(major) * 65536) + (int)(str2l(minor) * 256) + (int)str2l(patch)); } @@ -272,14 +274,24 @@ char *ebpf_kernel_suffix(int version, int isrh) //---------------------------------------------------------------------------------------------------------------------- -int ebpf_update_kernel(ebpf_data_t *ed) +/** + * Update Kernel + * + * Update string used to load eBPF programs + * + * @param ks vector to store the value + * @param length available length to store kernel + * @param isrh Is a Red Hat distribution? + * @param version the kernel version + */ +void ebpf_update_kernel(char *ks, size_t length, int isrh, int version) { - char *kernel = ebpf_kernel_suffix(ed->running_on_kernel, (ed->isrh < 0) ? 0 : 1); - size_t length = strlen(kernel); - strncpyz(ed->kernel_string, kernel, length); - ed->kernel_string[length] = '\0'; - - return 0; + char *kernel = ebpf_kernel_suffix(version, (isrh < 0) ? 0 : 1); + size_t len = strlen(kernel); + if (len > length) + len = length - 1; + strncpyz(ks, kernel, len); + ks[len] = '\0'; } static int select_file(char *name, const char *program, size_t length, int mode, char *kernel_string) @@ -307,18 +319,27 @@ void ebpf_update_map_sizes(struct bpf_object *program, ebpf_module_t *em) if (!maps) return; + uint32_t apps_type = NETDATA_EBPF_MAP_PID | NETDATA_EBPF_MAP_RESIZABLE; bpf_map__for_each(map, program) { const char *map_name = bpf_map__name(map); int i = 0; ; while (maps[i].name) { ebpf_local_maps_t *w = &maps[i]; - if (w->user_input != w->internal_input && !strcmp(w->name, map_name)) { + if (w->type & NETDATA_EBPF_MAP_RESIZABLE) { + if (!strcmp(w->name, map_name)) { + if (w->user_input && w->user_input != w->internal_input) { #ifdef NETDATA_INTERNAL_CHECKS - info("Changing map %s from size %u to %u ", map_name, w->internal_input, w->user_input); + info("Changing map %s from size %u to %u ", map_name, w->internal_input, w->user_input); #endif - bpf_map__resize(map, w->user_input); + bpf_map__resize(map, w->user_input); + } else if (((w->type & apps_type) == apps_type) && (!em->apps_charts) && (!em->cgroup_charts)) { + w->user_input = ND_EBPF_DEFAULT_MIN_PID; + bpf_map__resize(map, w->user_input); + } + } } + i++; } } @@ -377,8 +398,59 @@ static struct bpf_link **ebpf_attach_programs(struct bpf_object *obj, size_t len return links; } +static void ebpf_update_maps(ebpf_module_t *em, struct bpf_object *obj) +{ + if (!em->maps) + return; + + ebpf_local_maps_t *maps = em->maps; + struct bpf_map *map; + bpf_map__for_each(map, obj) + { + int fd = bpf_map__fd(map); + if (maps) { + const char *map_name = bpf_map__name(map); + int j = 0; ; + while (maps[j].name) { + ebpf_local_maps_t *w = &maps[j]; + if (w->map_fd == ND_EBPF_MAP_FD_NOT_INITIALIZED && !strcmp(map_name, w->name)) + w->map_fd = fd; + + j++; + } + } + } +} + +static void ebpf_update_controller(ebpf_module_t *em, struct bpf_object *obj) +{ + ebpf_local_maps_t *maps = em->maps; + if (!maps) + return; + + struct bpf_map *map; + bpf_map__for_each(map, obj) + { + size_t i = 0; + while (maps[i].name) { + ebpf_local_maps_t *w = &maps[i]; + if (w->map_fd != ND_EBPF_MAP_FD_NOT_INITIALIZED && (w->type & NETDATA_EBPF_MAP_CONTROLLER)) { + w->type &= ~NETDATA_EBPF_MAP_CONTROLLER; + w->type |= NETDATA_EBPF_MAP_CONTROLLER_UPDATED; + + uint32_t key = NETDATA_CONTROLLER_APPS_ENABLED; + int value = em->apps_charts | em->cgroup_charts; + int ret = bpf_map_update_elem(w->map_fd, &key, &value, 0); + if (ret) + error("Add key(%u) for controller table failed.", key); + } + i++; + } + } +} + struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, char *kernel_string, - struct bpf_object **obj, int *map_fd) + struct bpf_object **obj) { char lpath[4096]; char lname[128]; @@ -403,13 +475,8 @@ struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, char * return NULL; } - struct bpf_map *map; - size_t i = 0; - bpf_map__for_each(map, *obj) - { - map_fd[i] = bpf_map__fd(map); - i++; - } + ebpf_update_maps(em, *obj); + ebpf_update_controller(em, *obj); size_t count_programs = ebpf_count_programs(*obj); @@ -462,7 +529,7 @@ void ebpf_update_names(ebpf_specify_name_t *opt, ebpf_module_t *em) //---------------------------------------------------------------------------------------------------------------------- -void ebpf_mount_config_name(char *filename, size_t length, char *path, char *config) +void ebpf_mount_config_name(char *filename, size_t length, char *path, const char *config) { snprintf(filename, length, "%s/ebpf.d/%s", path, config); } @@ -475,7 +542,7 @@ int ebpf_load_config(struct config *config, char *filename) static netdata_run_mode_t ebpf_select_mode(char *mode) { - if (!strcasecmp(mode, "return")) + if (!strcasecmp(mode,EBPF_CFG_LOAD_MODE_RETURN )) return MODE_RETURN; else if (!strcasecmp(mode, "dev")) return MODE_DEVMODE; @@ -483,17 +550,31 @@ static netdata_run_mode_t ebpf_select_mode(char *mode) return MODE_ENTRY; } -void ebpf_update_module_using_config(ebpf_module_t *modules, struct config *cfg) +static void ebpf_select_mode_string(char *output, size_t len, netdata_run_mode_t sel) { - char *mode = appconfig_get(cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_LOAD_MODE, EBPF_CFG_LOAD_MODE_DEFAULT); + if (sel == MODE_RETURN) + strncpyz(output, EBPF_CFG_LOAD_MODE_RETURN, len); + else + strncpyz(output, EBPF_CFG_LOAD_MODE_DEFAULT, len); +} + +/** + * @param modules structure that will be updated + */ +void ebpf_update_module_using_config(ebpf_module_t *modules) +{ + char default_value[EBPF_MAX_MODE_LENGTH + 1]; + ebpf_select_mode_string(default_value, EBPF_MAX_MODE_LENGTH, modules->mode); + char *mode = appconfig_get(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_LOAD_MODE, default_value); modules->mode = ebpf_select_mode(mode); - modules->update_time = (int)appconfig_get_number(cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_UPDATE_EVERY, 1); + modules->update_every = (int)appconfig_get_number(modules->cfg, EBPF_GLOBAL_SECTION, + EBPF_CFG_UPDATE_EVERY, modules->update_every); - modules->apps_charts = appconfig_get_boolean(cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_APPLICATION, - CONFIG_BOOLEAN_YES); + modules->apps_charts = appconfig_get_boolean(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_APPLICATION, + modules->apps_charts); - modules->pid_map_size = (uint32_t)appconfig_get_number(cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, + modules->pid_map_size = (uint32_t)appconfig_get_number(modules->cfg, EBPF_GLOBAL_SECTION, EBPF_CFG_PID_SIZE, modules->pid_map_size); } @@ -507,20 +588,249 @@ void ebpf_update_module_using_config(ebpf_module_t *modules, struct config *cfg) * update the variables. * * @param em the module structure - * @param cfg the configuration structure - * @param cfg_file the filename to load */ -void ebpf_update_module(ebpf_module_t *em, struct config *cfg, char *cfg_file) +void ebpf_update_module(ebpf_module_t *em) { char filename[FILENAME_MAX+1]; - ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_user_config_dir, cfg_file); - if (!ebpf_load_config(cfg, filename)) { - ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_stock_config_dir, cfg_file); - if (!ebpf_load_config(cfg, filename)) { - error("Cannot load the ebpf configuration file %s", cfg_file); + ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_user_config_dir, em->config_file); + if (!ebpf_load_config(em->cfg, filename)) { + ebpf_mount_config_name(filename, FILENAME_MAX, ebpf_stock_config_dir, em->config_file); + if (!ebpf_load_config(em->cfg, filename)) { + error("Cannot load the ebpf configuration file %s", em->config_file); return; } } - ebpf_update_module_using_config(em, cfg); + ebpf_update_module_using_config(em); +} + +//---------------------------------------------------------------------------------------------------------------------- + +/** + * Load Address + * + * Helper used to get address from /proc/kallsym + * + * @param fa address structure + * @param fd file descriptor loaded inside kernel. + */ +void ebpf_load_addresses(ebpf_addresses_t *fa, int fd) +{ + if (fa->addr) + return ; + + procfile *ff = procfile_open("/proc/kallsyms", " \t:", PROCFILE_FLAG_DEFAULT); + if (!ff) + return; + + ff = procfile_readall(ff); + if (!ff) + return; + + fa->hash = simple_hash(fa->function); + + size_t lines = procfile_lines(ff), l; + for(l = 0; l < lines ;l++) { + char *fcnt = procfile_lineword(ff, l, 2); + uint32_t hash = simple_hash(fcnt); + if (fa->hash == hash && !strcmp(fcnt, fa->function)) { + char addr[128]; + snprintf(addr, 127, "0x%s", procfile_lineword(ff, l, 0)); + fa->addr = (unsigned long) strtoul(addr, NULL, 16); + uint32_t key = 0; + bpf_map_update_elem(fd, &key, &fa->addr, BPF_ANY); + } + } + + procfile_close(ff); +} + +//---------------------------------------------------------------------------------------------------------------------- + +/** + * Fill Algorithms + * + * Set one unique dimension for all vector position. + * + * @param algorithms the output vector + * @param length number of elements of algorithms vector + * @param algorithm algorithm used on charts. +*/ +void ebpf_fill_algorithms(int *algorithms, size_t length, int algorithm) +{ + size_t i; + for (i = 0; i < length; i++) { + algorithms[i] = algorithm; + } +} + +/** + * Fill Histogram dimension + * + * Fill the histogram dimension with the specified ranges + */ +char **ebpf_fill_histogram_dimension(size_t maximum) +{ + char *dimensions[] = { "us", "ms", "s"}; + int previous_dim = 0, current_dim = 0; + uint32_t previous_level = 1000, current_level = 1000; + uint32_t previous_divisor = 1, current_divisor = 1; + uint32_t current = 1, previous = 0; + uint32_t selector; + char **out = callocz(maximum, sizeof(char *)); + char range[128]; + size_t end = maximum - 1; + for (selector = 0; selector < end; selector++) { + snprintf(range, 127, "%u%s->%u%s", previous/previous_divisor, dimensions[previous_dim], + current/current_divisor, dimensions[current_dim]); + out[selector] = strdupz(range); + previous = current; + current <<= 1; + + if (previous_dim != 2 && previous > previous_level) { + previous_dim++; + + previous_divisor *= 1000; + previous_level *= 1000; + } + + if (current_dim != 2 && current > current_level) { + current_dim++; + + current_divisor *= 1000; + current_level *= 1000; + } + } + snprintf(range, 127, "%u%s->+Inf", previous/previous_divisor, dimensions[previous_dim]); + out[selector] = strdupz(range); + + return out; +} + +/** + * Histogram dimension cleanup + * + * Cleanup dimensions allocated with function ebpf_fill_histogram_dimension + * + * @param ptr + * @param length + */ +void ebpf_histogram_dimension_cleanup(char **ptr, size_t length) +{ + size_t i; + for (i = 0; i < length; i++) { + freez(ptr[i]); + } + freez(ptr); +} + +//---------------------------------------------------------------------------------------------------------------------- + +/** + * Open tracepoint path + * + * @param filename pointer to store the path + * @param length file length + * @param subsys is the name of your subsystem. + * @param eventname is the name of the event to trace. + * @param flags flags used with syscall open + * + * @return it returns a positive value on success and a negative otherwise. + */ +static inline int ebpf_open_tracepoint_path(char *filename, size_t length, char *subsys, char *eventname, int flags) +{ + snprintfz(filename, length, "%s/events/%s/%s/enable", NETDATA_DEBUGFS, subsys, eventname); + return open(filename, flags, 0); +} + +/** + * Is tracepoint enabled + * + * Check whether the tracepoint is enabled. + * + * @param subsys is the name of your subsystem. + * @param eventname is the name of the event to trace. + * + * @return it returns 1 when it is enabled, 0 when it is disabled and -1 on error. + */ +int ebpf_is_tracepoint_enabled(char *subsys, char *eventname) +{ + char text[FILENAME_MAX + 1]; + int fd = ebpf_open_tracepoint_path(text, FILENAME_MAX, subsys, eventname, O_RDONLY); + if (fd < 0) { + return -1; + } + + ssize_t length = read(fd, text, 1); + if (length != 1) { + close(fd); + return -1; + } + close(fd); + + return (text[0] == '1') ? CONFIG_BOOLEAN_YES : CONFIG_BOOLEAN_NO; +} + +/** + * Change Tracing values + * + * Change value for specific tracepoint enabling or disabling it according value given. + * + * @param subsys is the name of your subsystem. + * @param eventname is the name of the event to trace. + * @param value a value to enable (1) or disable (0) a tracepoint. + * + * @return It returns 0 on success and -1 otherwise + */ +static int ebpf_change_tracing_values(char *subsys, char *eventname, char *value) +{ + if (strcmp("0", value) && strcmp("1", value)) { + error("Invalid value given to either enable or disable a tracepoint."); + return -1; + } + + char filename[1024]; + int fd = ebpf_open_tracepoint_path(filename, 1023, subsys, eventname, O_WRONLY); + if (fd < 0) { + return -1; + } + + ssize_t written = write(fd, value, strlen(value)); + if (written < 0) { + close(fd); + return -1; + } + + close(fd); + return 0; +} + +/** + * Enable tracing values + * + * Enable a tracepoint on a system + * + * @param subsys is the name of your subsystem. + * @param eventname is the name of the event to trace. + * + * @return It returns 0 on success and -1 otherwise + */ +int ebpf_enable_tracing_values(char *subsys, char *eventname) +{ + return ebpf_change_tracing_values(subsys, eventname, "1"); +} + +/** + * Disable tracing values + * + * Disable tracing points enabled by collector + * + * @param subsys is the name of your subsystem. + * @param eventname is the name of the event to trace. + * + * @return It returns 0 on success and -1 otherwise + */ +int ebpf_disable_tracing_values(char *subsys, char *eventname) +{ + return ebpf_change_tracing_values(subsys, eventname, "0"); } diff --git a/libnetdata/ebpf/ebpf.h b/libnetdata/ebpf/ebpf.h index bc55d9595..73128f529 100644 --- a/libnetdata/ebpf/ebpf.h +++ b/libnetdata/ebpf/ebpf.h @@ -5,6 +5,7 @@ #include <bpf/bpf.h> #include <bpf/libbpf.h> +#include <stdlib.h> // Necessary for stdtoul #define NETDATA_DEBUGFS "/sys/kernel/debug/tracing/" #define NETDATA_KALLSYMS "/proc/kallsyms" @@ -14,10 +15,12 @@ #define EBPF_CFG_LOAD_MODE "ebpf load mode" #define EBPF_CFG_LOAD_MODE_DEFAULT "entry" #define EBPF_CFG_LOAD_MODE_RETURN "return" +#define EBPF_MAX_MODE_LENGTH 6 #define EBPF_CFG_UPDATE_EVERY "update every" #define EBPF_CFG_PID_SIZE "pid table size" #define EBPF_CFG_APPLICATION "apps" +#define EBPF_CFG_CGROUP "cgroups" /** * The next magic number is got doing the following math: @@ -57,6 +60,13 @@ #define NETDATA_EBPF_KERNEL_5_10 330240 /** + * Kernel 5.0 + * + * 327680 = 5*65536 +256*0 + */ +#define NETDATA_EBPF_KERNEL_5_0 327680 + +/** * Kernel 4.17 * * 266496 = 4*65536 + 17*256 @@ -80,6 +90,16 @@ #define VERSION_STRING_LEN 256 #define EBPF_KERNEL_REJECT_LIST_FILE "ebpf_kernel_reject_list.txt" +#define ND_EBPF_DEFAULT_MIN_PID 1U +#define ND_EBPF_MAP_FD_NOT_INITIALIZED (int)-1 + +typedef struct ebpf_addresses { + char *function; + uint32_t hash; + // We use long as address, because it matches system length + unsigned long addr; +} ebpf_addresses_t; + extern char *ebpf_user_config_dir; extern char *ebpf_stock_config_dir; @@ -99,10 +119,26 @@ typedef enum { #define ND_EBPF_DEFAULT_PID_SIZE 32768U +enum netdata_ebpf_map_type { + NETDATA_EBPF_MAP_STATIC = 0, + NETDATA_EBPF_MAP_RESIZABLE = 1, + NETDATA_EBPF_MAP_CONTROLLER = 2, + NETDATA_EBPF_MAP_CONTROLLER_UPDATED = 4, + NETDATA_EBPF_MAP_PID = 8 +}; + +enum netdata_controller { + NETDATA_CONTROLLER_APPS_ENABLED, + + NETDATA_CONTROLLER_END +}; + typedef struct ebpf_local_maps { char *name; uint32_t internal_input; uint32_t user_input; + uint32_t type; + int map_fd; } ebpf_local_maps_t; typedef struct ebpf_specify_name { @@ -117,9 +153,10 @@ typedef struct ebpf_module { const char *config_name; int enabled; void *(*start_routine)(void *); - int update_time; + int update_every; int global_charts; int apps_charts; + int cgroup_charts; netdata_run_mode_t mode; uint32_t thread_id; int optional; @@ -127,23 +164,46 @@ typedef struct ebpf_module { ebpf_local_maps_t *maps; ebpf_specify_name_t *names; uint32_t pid_map_size; + struct config *cfg; + const char *config_file; } ebpf_module_t; -extern int get_kernel_version(char *out, int size); +extern int ebpf_get_kernel_version(); extern int get_redhat_release(); extern int has_condition_to_run(int version); extern char *ebpf_kernel_suffix(int version, int isrh); -extern int ebpf_update_kernel(ebpf_data_t *ef); +extern void ebpf_update_kernel(char *ks, size_t length, int isrh, int version); extern struct bpf_link **ebpf_load_program(char *plugins_dir, ebpf_module_t *em, char *kernel_string, - struct bpf_object **obj, - int *map_fd); + struct bpf_object **obj); -extern void ebpf_mount_config_name(char *filename, size_t length, char *path, char *config); +extern void ebpf_mount_config_name(char *filename, size_t length, char *path, const char *config); extern int ebpf_load_config(struct config *config, char *filename); -extern void ebpf_update_module_using_config(ebpf_module_t *modules, struct config *cfg); -extern void ebpf_update_module(ebpf_module_t *em, struct config *cfg, char *cfg_file); +extern void ebpf_update_module(ebpf_module_t *em); extern void ebpf_update_names(ebpf_specify_name_t *opt, ebpf_module_t *em); +extern void ebpf_load_addresses(ebpf_addresses_t *fa, int fd); +extern void ebpf_fill_algorithms(int *algorithms, size_t length, int algorithm); +extern char **ebpf_fill_histogram_dimension(size_t maximum); + +// Histogram +#define NETDATA_EBPF_HIST_MAX_BINS 24UL +#define NETDATA_DISK_MAX 256U +#define NETDATA_DISK_HISTOGRAM_LENGTH (NETDATA_DISK_MAX * NETDATA_EBPF_HIST_MAX_BINS) + +typedef struct netdata_ebpf_histogram { + char *name; + char *title; + int order; + uint64_t histogram[NETDATA_EBPF_HIST_MAX_BINS]; +} netdata_ebpf_histogram_t; + +extern void ebpf_histogram_dimension_cleanup(char **ptr, size_t length); + +// Tracepoint helpers +// For more information related to tracepoints read https://www.kernel.org/doc/html/latest/trace/tracepoints.html +extern int ebpf_is_tracepoint_enabled(char *subsys, char *eventname); +extern int ebpf_enable_tracing_values(char *subsys, char *eventname); +extern int ebpf_disable_tracing_values(char *subsys, char *eventname); #endif /* NETDATA_EBPF_H */ |