Skip to content

Preflight checks for BTF support #1120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 26, 2023
Merged
3 changes: 3 additions & 0 deletions ansible/group_vars/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ runtime_command: docker
runtime_as_root: false
runtime_socket: /var/run/docker.sock

excluded_pairs:
- ['ubuntu-1804-lts', 'core_bpf']

virtual_machines:
rhel:
project: rhel-cloud
Expand Down
3 changes: 2 additions & 1 deletion ansible/roles/create-vm/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@
loop_control:
loop_var: gcp_zone
register: instance_result
when: not condition
when: >
not condition and [vm_family, vm_collection_method] not in excluded_pairs
vars:
condition: "{{ (instance_result | default({'changed': false})).changed }}"

Expand Down
2 changes: 1 addition & 1 deletion collector/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ else()
target_link_libraries(collector_lib libprotobuf.a libcares.a)
endif()

target_link_libraries(collector_lib z ssl crypto curl)
target_link_libraries(collector_lib z ssl crypto curl bpf)

add_executable(collector collector.cpp)
target_link_libraries(collector collector_lib)
Expand Down
40 changes: 35 additions & 5 deletions collector/lib/HostHeuristics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,42 @@ class Heuristic {
};

class CollectionHeuristic : public Heuristic {
// If we're configured to use eBPF but the host we're running on
// does not support it, we can try to use kernel modules instead.
// The exception to this is COS, where third party modules are not
// supported, so there is nothing we can do and must exit.
void Process(HostInfo& host, const CollectorConfig& config, HostConfig* hconfig) const {
if (config.UseEbpf() && !host.HasEBPFSupport()) {
// If we're configured to use eBPF with BTF, we try to be conservative
// and fail instead of falling-back to other methods.
if (config.GetCollectionMethod() == CORE_BPF) {
if (!host.HasEBPFSupport()) {
CLOG(FATAL) << host.GetDistro() << " " << host.GetKernelVersion().release
<< " does not support eBPF, which is a requirement for core_bpf collection. "
<< "HINT: You may want to use kernel module based collection "
<< "with collector.collectionMethod=KERNEL_MODULE.";
}

if (!host.HasBTFSymbols()) {
CLOG(FATAL) << "Missing BTF symbols, core_bpf is not available. "
<< "They can be provided by the kernel when configured with DEBUG_INFO_BTF, "
<< "or as file. "
<< "HINT: You may alternatively want to use eBPF based collection "
<< "with collector.collectionMethod=EBPF.";
;
}

if (!host.HasBPFRingBufferSupport()) {
CLOG(FATAL) << "Missing RingBuffer support, core_bpf is not available. "
<< "HINT: You may alternatively want to use eBPF based collection "
<< "with collector.collectionMethod=EBPF.";
}

if (!host.HasBPFTracingSupport()) {
CLOG(FATAL) << "Missing BPF tracepoint support.";
}
}

// If we're configured to use eBPF but the host we're running on
// does not support it, we can try to use kernel modules instead.
// The exception to this is COS, where third party modules are not
// supported, so there is nothing we can do and must exit.
if ((config.GetCollectionMethod() == EBPF) && !host.HasEBPFSupport()) {
if (host.IsCOS()) {
CLOG(FATAL) << host.GetDistro() << " does not support third-party kernel modules or the required eBPF features.";
}
Expand Down
69 changes: 69 additions & 0 deletions collector/lib/HostInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ You should have received a copy of the GNU General Public License along with thi

#include <fstream>

#include <bpf/libbpf.h>
#include <linux/bpf.h>

#include "Logging.h"

namespace collector {
Expand Down Expand Up @@ -179,6 +182,72 @@ bool HostInfo::HasEBPFSupport() {
return collector::hasEBPFSupport(kernel, GetOSID());
}

bool HostInfo::HasBTFSymbols() {
// This list is taken from libbpf
const char* locations[] = {
/* try canonical vmlinux BTF through sysfs first */
"/sys/kernel/btf/vmlinux",
/* fall back to trying to find vmlinux on disk otherwise */
"/boot/vmlinux-%1$s",
"/lib/modules/%1$s/vmlinux-%1$s",
"/lib/modules/%1$s/build/vmlinux",
"/usr/lib/modules/%1$s/kernel/vmlinux",
"/usr/lib/debug/boot/vmlinux-%1$s",
"/usr/lib/debug/boot/vmlinux-%1$s.debug",
"/usr/lib/debug/lib/modules/%1$s/vmlinux",
0};

char path[PATH_MAX + 1];
const char* const* location;

for (location = locations; *location; location++) {
snprintf(path, PATH_MAX, *location, kernel_version_.release.c_str());
std::string host_path = GetHostPath(path);

if (faccessat(AT_FDCWD, host_path.c_str(), R_OK, AT_EACCESS) == 0) {
CLOG(DEBUG) << "BTF symbols found in " << host_path;
return true;

} else {
if (errno == ENOTDIR || errno == ENOENT) {
CLOG(DEBUG) << host_path << " does not exist";
} else {
CLOG(WARNING) << "Unable to access " << host_path << ": " << StrError();
}
}
}

CLOG(DEBUG) << "Unable to find BTF symbols in any of the usual locations.";

return false;
}

bool HostInfo::HasBPFRingBufferSupport() {
int res;

res = libbpf_probe_bpf_map_type(BPF_MAP_TYPE_RINGBUF, NULL);

if (res < 0) {
CLOG(WARNING) << "Unable to check for the BPF RingBuffer availability. "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case libbpf says feature detection may failed, makes sense to report errno?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a reference to the errno availability in the docs for that case. And the code suggests that it does not reflect the actual error reason.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one [1]? It seems this function does sys_bpf_fd and returns -errno if fd < 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errno is tested underneath, but when the error if reported at the API level, I am afraid this information is lost.

<< "Assuming it is available.";
}

return res != 0;
}

bool HostInfo::HasBPFTracingSupport() {
int res;

res = libbpf_probe_bpf_prog_type(BPF_PROG_TYPE_TRACING, NULL);

if (res < 0) {
CLOG(WARNING) << "Unable to check for the BPF tracepoint program type support. "
<< "Assuming it is available.";
}

return res != 0;
}

bool HostInfo::IsUEFI() {
struct stat sb;
std::string efi_path = GetHostPath("/sys/firmware/efi");
Expand Down
9 changes: 9 additions & 0 deletions collector/lib/HostInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ class HostInfo {
// not support eBPF)
bool HasEBPFSupport();

// Search for a source of BTF symbols (similar to what libbpf does)
bool HasBTFSymbols();

// Check for RingBuffer support, which is required by the modern probe.
bool HasBPFRingBufferSupport();

// Check for BPF tracepoint program type support
bool HasBPFTracingSupport();

// The system was booted in UEFI mode.
virtual bool IsUEFI();

Expand Down
1 change: 0 additions & 1 deletion kernel-modules/build/fc36.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ RUN dnf -y install \
gcc-c++ \
llvm \
clang \
elfutils-libelf \
elfutils-libelf-devel \
kmod && \
# We trick Debian builds into thinking they have the required GCC binary
Expand Down
1 change: 0 additions & 1 deletion kernel-modules/build/rhel7.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ RUN yum makecache && \
make \
gcc-c++ \
llvm-toolset-7.0 \
elfutils-libelf \
elfutils-libelf-devel \
kmod

Expand Down
1 change: 0 additions & 1 deletion kernel-modules/build/rhel8.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ RUN dnf -y update && \
gcc-c++ \
llvm \
clang \
elfutils-libelf \
elfutils-libelf-devel \
kmod && \
# We trick Debian builds into thinking they have the required GCC binaries
Expand Down