summaryrefslogtreecommitdiffstats
path: root/libnetdata
diff options
context:
space:
mode:
authorVladimir Kobal <vlad@prokk.net>2020-07-16 15:10:35 +0300
committerGitHub <noreply@github.com>2020-07-16 15:10:35 +0300
commit3e84239ff64ececa6bd54bbddbc9e3d22542dfa9 (patch)
treea86249faf962bec50715e925a432bb07efe3d102 /libnetdata
parent1b606bc1b2a16dfbcc8b5c7ed780906b482eef11 (diff)
Use the libbpf library for the eBPF plugin (#9490)
Diffstat (limited to 'libnetdata')
-rw-r--r--libnetdata/ebpf/README.md1
-rw-r--r--libnetdata/ebpf/ebpf.c165
-rw-r--r--libnetdata/ebpf/ebpf.h43
-rw-r--r--libnetdata/ebpf/libbpf.c.diff229
-rw-r--r--libnetdata/libnetdata.h2
5 files changed, 321 insertions, 119 deletions
diff --git a/libnetdata/ebpf/README.md b/libnetdata/ebpf/README.md
index e69de29bb2..72446eb5bd 100644
--- a/libnetdata/ebpf/README.md
+++ b/libnetdata/ebpf/README.md
@@ -0,0 +1 @@
+[![analytics](https://www.google-analytics.com/collect?v=1&aip=1&t=pageview&_s=1&ds=github&dr=https%3A%2F%2Fgithub.com%2Fnetdata%2Fnetdata&dl=https%3A%2F%2Fmy-netdata.io%2Fgithub%2Flibnetdata%2Febpf%2FREADME&_u=MAC~&cid=5792dfd7-8dc4-476b-af31-da2fdb9f93d2&tid=UA-64295674-3)](<>)
diff --git a/libnetdata/ebpf/ebpf.c b/libnetdata/ebpf/ebpf.c
index 75a9f5dcb3..0d7af7068a 100644
--- a/libnetdata/ebpf/ebpf.c
+++ b/libnetdata/ebpf/ebpf.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -5,10 +7,11 @@
#include "../libnetdata.h"
-static int clean_kprobe_event(FILE *out, char *filename, char *father_pid, netdata_ebpf_events_t *ptr) {
- int fd = open(filename, O_WRONLY | O_APPEND, 0);
+static int clean_kprobe_event(FILE *out, char *filename, char *father_pid, netdata_ebpf_events_t *ptr)
+{
+ int fd = open(filename, O_WRONLY | O_APPEND, 0);
if (fd < 0) {
- if(out) {
+ if (out) {
fprintf(out, "Cannot open %s : %s\n", filename, strerror(errno));
}
return 1;
@@ -20,10 +23,10 @@ static int clean_kprobe_event(FILE *out, char *filename, char *father_pid, netda
if (length > 0) {
ssize_t written = write(fd, cmd, strlen(cmd));
if (written < 0) {
- if(out) {
- fprintf(out
- , "Cannot remove the event (%d, %d) '%s' from %s : %s\n"
- , getppid(), getpid(), cmd, filename, strerror((int)errno));
+ if (out) {
+ fprintf(
+ out, "Cannot remove the event (%d, %d) '%s' from %s : %s\n", getppid(), getpid(), cmd, filename,
+ strerror((int)errno));
}
ret = 1;
}
@@ -34,16 +37,17 @@ static int clean_kprobe_event(FILE *out, char *filename, char *father_pid, netda
return ret;
}
-int clean_kprobe_events(FILE *out, int pid, netdata_ebpf_events_t *ptr) {
+int clean_kprobe_events(FILE *out, int pid, netdata_ebpf_events_t *ptr)
+{
debug(D_EXIT, "Cleaning parent process events.");
- char filename[FILENAME_MAX +1];
+ char filename[FILENAME_MAX + 1];
snprintf(filename, FILENAME_MAX, "%s%s", NETDATA_DEBUGFS, "kprobe_events");
char removeme[16];
- snprintf(removeme, 15,"%d", pid);
+ snprintf(removeme, 15, "%d", pid);
int i;
- for (i = 0 ; ptr[i].name ; i++) {
+ for (i = 0; ptr[i].name; i++) {
if (clean_kprobe_event(out, filename, removeme, &ptr[i])) {
break;
}
@@ -54,7 +58,8 @@ int clean_kprobe_events(FILE *out, int pid, netdata_ebpf_events_t *ptr) {
//----------------------------------------------------------------------------------------------------------------------
-int get_kernel_version(char *out, int size) {
+int get_kernel_version(char *out, int size)
+{
char major[16], minor[16], patch[16];
char ver[256];
char *version = ver;
@@ -73,12 +78,14 @@ int get_kernel_version(char *out, int size) {
close(fd);
char *move = major;
- while (*version && *version != '.') *move++ = *version++;
+ while (*version && *version != '.')
+ *move++ = *version++;
*move = '\0';
version++;
move = minor;
- while (*version && *version != '.') *move++ = *version++;
+ while (*version && *version != '.')
+ *move++ = *version++;
*move = '\0';
if (*version)
@@ -87,27 +94,28 @@ int get_kernel_version(char *out, int size) {
return -1;
move = patch;
- while (*version && *version != '\n') *move++ = *version++;
+ while (*version && *version != '\n')
+ *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));
+ return ((int)(str2l(major) * 65536) + (int)(str2l(minor) * 256) + (int)str2l(patch));
}
int get_redhat_release()
{
char buffer[256];
- int major,minor;
+ int major, minor;
FILE *fp = fopen("/etc/redhat-release", "r");
if (fp) {
major = 0;
minor = -1;
size_t length = fread(buffer, sizeof(char), 255, fp);
- if (length > 4 ) {
+ if (length > 4) {
buffer[length] = '\0';
char *end = strchr(buffer, '.');
char *start;
@@ -117,13 +125,13 @@ int get_redhat_release()
if (end > buffer) {
start = end - 1;
- major = strtol( start, NULL, 10);
+ major = strtol(start, NULL, 10);
start = ++end;
end++;
- if(end) {
+ if (end) {
end = 0x00;
- minor = strtol( start, NULL, 10);
+ minor = strtol(start, NULL, 10);
} else {
minor = -1;
}
@@ -132,19 +140,21 @@ int get_redhat_release()
}
fclose(fp);
- return ((major*256) + minor);
+ return ((major * 256) + minor);
} else {
return -1;
}
}
-static int has_ebpf_kernel_version(int version) {
- //Kernel 4.11.0 or RH > 7.5
- return (version >= NETDATA_MINIMUM_EBPF_KERNEL || get_redhat_release() >= NETDATA_MINIMUM_RH_VERSION);
+static int has_ebpf_kernel_version(int version)
+{
+ // Kernel 4.11.0 or RH > 7.5
+ return (version >= NETDATA_MINIMUM_EBPF_KERNEL || get_redhat_release() >= NETDATA_MINIMUM_RH_VERSION);
}
-int has_condition_to_run(int version) {
- if(!has_ebpf_kernel_version(version))
+int has_condition_to_run(int version)
+{
+ if (!has_ebpf_kernel_version(version))
return 0;
return 1;
@@ -152,7 +162,8 @@ int has_condition_to_run(int version) {
//----------------------------------------------------------------------------------------------------------------------
-char *ebpf_library_suffix(int version, int isrh) {
+char *ebpf_kernel_suffix(int version, int isrh)
+{
if (isrh) {
if (version >= NETDATA_EBPF_KERNEL_4_11)
return "4.18.0";
@@ -172,101 +183,63 @@ char *ebpf_library_suffix(int version, int isrh) {
//----------------------------------------------------------------------------------------------------------------------
-int ebpf_load_libraries(ebpf_functions_t *ef, char *libbase, char *pluginsdir)
+int ebpf_update_kernel(ebpf_data_t *ed)
{
- char *err = NULL;
- char lpath[4096];
- char netdatasl[128];
- void *libnetdata;
-
- snprintf(netdatasl, 127, "%s.%s", libbase, ef->kernel_string);
- snprintf(lpath, 4095, "%s/%s", pluginsdir, netdatasl);
- libnetdata = dlopen(lpath, RTLD_LAZY);
- if (!libnetdata) {
- info("Cannot load library %s for the current kernel.", lpath);
-
- //Update kernel
- char *library = ebpf_library_suffix(ef->running_on_kernel, (ef->isrh < 0)?0:1);
- size_t length = strlen(library);
- strncpyz(ef->kernel_string, library, length);
- ef->kernel_string[length] = '\0';
-
- //Try to load the default version
- snprintf(netdatasl, 127, "%s.%s", libbase, ef->kernel_string);
- snprintf(lpath, 4095, "%s/%s", pluginsdir, netdatasl);
- libnetdata = dlopen(lpath, RTLD_LAZY);
- if (!libnetdata) {
- error("Cannot load %s default library.", lpath);
- return -1;
- } else {
- info("Default shared library %s loaded with success.", lpath);
- ef->libnetdata = libnetdata;
- }
- } else {
- info("Current shared library %s loaded with success.", lpath);
- ef->libnetdata = libnetdata;
- }
-
- ef->load_bpf_file = dlsym(libnetdata, "load_bpf_file");
- if ((err = dlerror()) != NULL) {
- error("Cannot find load_bpf_file: %s", err);
- return -1;
- }
-
- ef->bpf_map_lookup_elem = dlsym(libnetdata, "bpf_map_lookup_elem");
- if ((err = dlerror()) != NULL) {
- error("Cannot find bpf_map_lookup_elem: %s", err);
- return -1;
- }
-
- ef->bpf_map_delete_elem = dlsym(libnetdata, "bpf_map_delete_elem");
- if ((err = dlerror()) != NULL) {
- error("Cannot find bpf_map_delete_elem: %s", err);
- return -1;
- }
-
- ef->bpf_map_get_next_key = dlsym(libnetdata, "bpf_map_get_next_key");
- if ((err = dlerror()) != NULL) {
- error("Cannot find bpf_map_delete_elem: %s", err);
- return -1;
- }
+ 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;
}
-static int select_file(char *name, const char *program, size_t length, int mode , char *kernel_string) {
+static int select_file(char *name, const char *program, size_t length, int mode, char *kernel_string)
+{
int ret = -1;
if (!mode)
ret = snprintf(name, length, "rnetdata_ebpf_%s.%s.o", program, kernel_string);
- else if(mode == 1)
+ else if (mode == 1)
ret = snprintf(name, length, "dnetdata_ebpf_%s.%s.o", program, kernel_string);
- else if(mode == 2)
+ else if (mode == 2)
ret = snprintf(name, length, "pnetdata_ebpf_%s.%s.o", program, kernel_string);
return ret;
}
-int ebpf_load_program(char *plugins_dir,
- int event_id, int mode ,
- char *kernel_string,
- const char *name,
- int *map_fd,
- int (*load_bpf_file)(int *, char *, int))
+int ebpf_load_program(char *plugins_dir, int event_id, int mode, char *kernel_string, const char *name, int *map_fd)
{
+ UNUSED(event_id);
+
char lpath[4096];
char lname[128];
+ struct bpf_object *obj;
+ int prog_fd;
int test = select_file(lname, name, (size_t)127, mode, kernel_string);
if (test < 0 || test > 127)
return -1;
- snprintf(lpath, 4096, "%s/%s", plugins_dir, lname);
- if (load_bpf_file(map_fd, lpath, event_id)) {
+ snprintf(lpath, 4096, "%s/%s", plugins_dir, lname);
+ if (bpf_prog_load(lpath, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd)) {
info("Cannot load program: %s", lpath);
return -1;
} else {
info("The eBPF program %s was loaded with success.", name);
}
+ struct bpf_map *map;
+ size_t i = 0;
+ bpf_map__for_each(map, obj)
+ {
+ map_fd[i] = bpf_map__fd(map);
+ i++;
+ }
+
+ struct bpf_program *prog;
+ bpf_object__for_each_program(prog, obj)
+ {
+ bpf_program__attach(prog);
+ }
+
return 0;
}
diff --git a/libnetdata/ebpf/ebpf.h b/libnetdata/ebpf/ebpf.h
index 6e01786c85..1003f9eda3 100644
--- a/libnetdata/ebpf/ebpf.h
+++ b/libnetdata/ebpf/ebpf.h
@@ -1,7 +1,12 @@
-#ifndef _NETDATA_EBPF_H_
-# define _NETDATA_EBPF_H_ 1
+// SPDX-License-Identifier: GPL-3.0-or-later
-# define NETDATA_DEBUGFS "/sys/kernel/debug/tracing/"
+#ifndef NETDATA_EBPF_H
+#define NETDATA_EBPF_H 1
+
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+#define NETDATA_DEBUGFS "/sys/kernel/debug/tracing/"
/**
* The next magic number is got doing the following math:
@@ -9,7 +14,7 @@
*
* For more details, please, read /usr/include/linux/version.h
*/
-# define NETDATA_MINIMUM_EBPF_KERNEL 264960
+#define NETDATA_MINIMUM_EBPF_KERNEL 264960
/**
* The RedHat magic number was got doing:
@@ -19,65 +24,57 @@
* For more details, please, read /usr/include/linux/version.h
* in any Red Hat installation.
*/
-# define NETDATA_MINIMUM_RH_VERSION 1797
+#define NETDATA_MINIMUM_RH_VERSION 1797
/**
* 2048 = 8*256 + 0
*/
-# define NETDATA_RH_8 2048
+#define NETDATA_RH_8 2048
/**
* Kernel 4.17
*
* 266496 = 4*65536 + 17*256
*/
-# define NETDATA_EBPF_KERNEL_4_17 266496
+#define NETDATA_EBPF_KERNEL_4_17 266496
/**
* Kernel 4.15
*
* 265984 = 4*65536 + 15*256
*/
-# define NETDATA_EBPF_KERNEL_4_15 265984
+#define NETDATA_EBPF_KERNEL_4_15 265984
/**
* Kernel 4.11
*
* 264960 = 4*65536 + 15*256
*/
-# define NETDATA_EBPF_KERNEL_4_11 264960
+#define NETDATA_EBPF_KERNEL_4_11 264960
typedef struct netdata_ebpf_events {
char type;
char *name;
} netdata_ebpf_events_t;
-typedef struct ebpf_functions {
- void *libnetdata;
- int (*load_bpf_file)(int *, char *, int);
- //Libbpf (It is necessary to have at least kernel 4.10)
- int (*bpf_map_lookup_elem)(int, const void *, void *);
- int (*bpf_map_delete_elem)(int fd, const void *key);
- int (*bpf_map_get_next_key)(int fd, const void *key, void *next_key);
-
+typedef struct ebpf_data {
int *map_fd;
char *kernel_string;
uint32_t running_on_kernel;
int isrh;
-} ebpf_functions_t;
+} ebpf_data_t;
extern int clean_kprobe_events(FILE *out, int pid, netdata_ebpf_events_t *ptr);
extern int get_kernel_version(char *out, int size);
extern int get_redhat_release();
extern int has_condition_to_run(int version);
-extern char *ebpf_library_suffix(int version, int isrh);
-extern int ebpf_load_libraries(ebpf_functions_t *ef, char *libbase, char *pluginsdir);
+extern char *ebpf_kernel_suffix(int version, int isrh);
+extern int ebpf_update_kernel(ebpf_data_t *ef);
extern int ebpf_load_program(char *plugins_dir,
int event_id, int mode,
char *kernel_string,
const char *name,
- int *map_fd,
- int (*load_bpf_file)(int *,char *, int));
+ int *map_fd);
-#endif
+#endif /* NETDATA_EBPF_H */
diff --git a/libnetdata/ebpf/libbpf.c.diff b/libnetdata/ebpf/libbpf.c.diff
new file mode 100644
index 0000000000..f014a7bc64
--- /dev/null
+++ b/libnetdata/ebpf/libbpf.c.diff
@@ -0,0 +1,229 @@
+--- a/src/libbpf.c
++++ b/src/libbpf.c
+@@ -620,6 +620,93 @@ bpf_object__init_prog_names(struct bpf_object *obj)
+ return 0;
+ }
+
++static __u32 choose_specific_version0(int fd, __u32 current)
++{
++ char ver[256];
++ __u32 v_major, v_minor, v_patch;
++ ssize_t len = read(fd, ver, sizeof(ver));
++ if (len < 0) {
++ return 0;
++ }
++ ver[len] = '\0';
++
++ char *first = strchr(ver, ' ');
++ if (!first) {
++ return 0;
++ }
++
++ first++;
++ char *version = strchr(first, ' ');
++ if (!version) {
++ return 0;
++ }
++
++ version++;
++ if (sscanf(version, "%u.%u.%u", &v_major, &v_minor, &v_patch) != 3)
++ return current;
++
++ return KERNEL_VERSION(v_major, v_minor, v_patch);
++}
++
++static __u32 choose_kernel_version(__u32 current)
++{
++ FILE *fp_d = fopen("/etc/debian_version","r");
++ int fp_u = open("/proc/version_signature", O_RDONLY);
++ FILE *fp_rh = fopen("/etc/redhat-release","r");
++ char tmp[32];
++ int de = 0;
++ __u32 ret;
++
++ if (!fp_d && !fp_rh && fp_u == -1)
++ return current;
++
++ struct utsname u;
++ uname(&u);
++
++ if (fp_d) {
++ fclose(fp_d);
++ de = 1;
++ }
++
++ if (fp_rh) {
++ fclose(fp_rh);
++ de = 0;
++ }
++
++ if ( fp_u > 0 ) {
++ ret = choose_specific_version0(fp_u, current);
++ close(fp_u);
++ return (!ret)?current:ret;
++ }
++
++ __u32 v_kernel,v_major, v_minor, v_patch;
++ __u32 r_kernel,r_major, r_minor, r_patch;
++
++ if (sscanf(u.release, "%u.%u.%u-%u", &v_kernel, &v_major, &v_minor, &v_patch) != 4)
++ return current;
++
++ int length = snprintf(tmp, 31, "%u.%u", v_kernel, v_major);
++ tmp[length] = '\0';
++
++ char *parse = strstr(u.version, tmp);
++ if (!parse) {
++ return current;
++ }
++
++ char *space = strchr(parse, ' ');
++ if(space) {
++ length = (int)(space - parse);
++ strncpy(tmp, parse, (size_t)length);
++ tmp[length] = '\0';
++ }
++
++ if (sscanf(tmp, "%u.%u.%u-%u", &r_kernel, &r_major, &r_minor, &r_patch) != 4)
++ return current;
++
++ ret = (de)?KERNEL_VERSION(r_kernel, r_major, r_minor):KERNEL_VERSION(r_kernel, r_major, r_minor) + r_patch;
++ return (ret > current)?ret:current;
++}
++
+ static __u32 get_kernel_version(void)
+ {
+ __u32 major, minor, patch;
+@@ -628,7 +715,11 @@ static __u32 get_kernel_version(void)
+ uname(&info);
+ if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
+ return 0;
+- return KERNEL_VERSION(major, minor, patch);
++
++ if (major < 5)
++ return choose_kernel_version(KERNEL_VERSION(major, minor, patch));
++ else
++ return KERNEL_VERSION(major, minor, patch);
+ }
+
+ static const struct btf_member *
+@@ -3239,6 +3330,10 @@ int bpf_map__resize(struct bpf_map *map, __u32 max_entries)
+ static int
+ bpf_object__probe_loading(struct bpf_object *obj)
+ {
++ // Don't probe loading for very old kernels. CentOS 7 can't load this probe.
++ if (obj->kern_version <= KERNEL_VERSION(3, 10, 0))
++ return 0;
++
+ struct bpf_load_program_attr attr;
+ char *cp, errmsg[STRERR_BUFSIZE];
+ struct bpf_insn insns[] = {
+@@ -7612,6 +7707,88 @@ static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
+ return pfd;
+ }
+
++static int perf_event_open_old_kprobe(bool retprobe, const char *name)
++{
++#define DEBUG_FS "/sys/kernel/debug/tracing/"
++ struct perf_event_attr attr = {};
++ char errmsg[STRERR_BUFSIZE];
++ int kfd = -1, ret = -1, err;
++ char event_alias[256], buf[PATH_MAX];
++ const char *event_prefix = "";
++ bool event_was_written = false;
++
++ kfd = open(DEBUG_FS "kprobe_events", O_WRONLY | O_APPEND, 0);
++ if (kfd < 0) {
++ pr_warn("failed to open '%s%s': %s\n",
++ DEBUG_FS,
++ "kprobe_events",
++ libbpf_strerror_r(kfd, errmsg, sizeof(errmsg)));
++ return kfd;
++ }
++
++ char type = retprobe ? 'r' : 'p';
++ snprintf(event_alias, sizeof(event_alias), "%c_netdata_%s_%d", type, name, getpid());
++
++#ifdef __x86_64__
++ if (strncmp(name, "sys_", 4) == 0) {
++ snprintf(buf, sizeof(buf), "%c:__x64_%s __x64_%s",
++ type, event_alias, name);
++ ret = write(kfd, buf, strlen(buf));
++
++ if (ret >= 0) {
++ event_was_written = true;
++ event_prefix = "__x64_";
++ }
++ }
++#endif
++
++ if (!event_was_written) {
++ snprintf(buf, sizeof(buf), "%c:%s %s",
++ type, event_alias, name);
++ ret = write(kfd, buf, strlen(buf));
++ if (ret < 0) {
++ pr_warn("failed to create kprobe '%s': %s\n",
++ name,
++ libbpf_strerror_r(ret, errmsg, sizeof(errmsg)));
++ close(kfd);
++ return ret;
++ }
++ }
++
++ close(kfd);
++
++ snprintf(buf, sizeof(buf), DEBUG_FS "events/kprobes/%s%s/id",
++ event_prefix,
++ event_alias);
++
++ ret = parse_uint_from_file(buf, "%d\n");
++ if (ret < 0) {
++ pr_warn("failed to determine event id: %s\n",
++ libbpf_strerror_r(ret, errmsg, sizeof(errmsg)));
++ return ret;
++ }
++ attr.config = ret;
++
++ attr.size = sizeof(attr);
++ attr.type = PERF_TYPE_TRACEPOINT;
++ attr.config1 = ptr_to_u64(name); /* kprobe_func */
++ attr.config2 = 0; /* kprobe_addr */
++
++ /* pid filter is meaningful only for uprobes */
++ kfd = syscall(__NR_perf_event_open, &attr,
++ -1 /* pid */,
++ 0 /* cpu */,
++ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
++ if (kfd < 0) {
++ err = -errno;
++ pr_warn("%s perf_event_open_old_kprobe() failed: %s\n",
++ "kprobe",
++ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
++ return err;
++ }
++ return kfd;
++}
++
+ struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
+ bool retprobe,
+ const char *func_name)
+@@ -7623,11 +7800,14 @@ struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
+ pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
+ 0 /* offset */, -1 /* pid */);
+ if (pfd < 0) {
+- pr_warn("program '%s': failed to create %s '%s' perf event: %s\n",
+- bpf_program__title(prog, false),
+- retprobe ? "kretprobe" : "kprobe", func_name,
+- libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+- return ERR_PTR(pfd);
++ pfd = perf_event_open_old_kprobe(retprobe, func_name);
++ if (pfd < 0) {
++ pr_warn("program '%s': failed to create %s '%s' perf event: %s\n",
++ bpf_program__title(prog, false),
++ retprobe ? "kretprobe" : "kprobe", func_name,
++ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
++ return ERR_PTR(pfd);
++ }
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h
index 75a0de9575..9ec6049e23 100644
--- a/libnetdata/libnetdata.h
+++ b/libnetdata/libnetdata.h
@@ -314,7 +314,9 @@ extern char *netdata_configured_host_prefix;
#include "log/log.h"
#include "procfile/procfile.h"
#include "dictionary/dictionary.h"
+#ifdef HAVE_LIBBPF
#include "ebpf/ebpf.h"
+#endif
#include "eval/eval.h"
#include "statistical/statistical.h"
#include "adaptive_resortable_list/adaptive_resortable_list.h"