summaryrefslogtreecommitdiffstats
path: root/libnetdata/ebpf
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-12-01 06:15:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-12-01 06:15:04 +0000
commite970e0b37b8bd7f246feb3f70c4136418225e434 (patch)
tree0b67c0ca45f56f2f9d9c5c2e725279ecdf52d2eb /libnetdata/ebpf
parentAdding upstream version 1.31.0. (diff)
downloadnetdata-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.c392
-rw-r--r--libnetdata/ebpf/ebpf.h76
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 */