Skip to content
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

tests(test/drivers): extend modern bpf test framework to all drivers (part 1) #798

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ jobs:
KERNELDIR=/lib/modules/$(ls /lib/modules)/build make -j4
make run-unit-tests

build-and-test-modern-bpf-x86:
name: build-and-test-modern-bpf-x86 😇 (bundled_deps)
# This job checks that we correctly run `scap-open` for all the 3 drivers.
test-scap-open-x86:
name: test-scap-open-x86 😆 (bundled_deps)
runs-on: ubuntu-22.04
needs: paths-filter
if: needs.paths-filter.outputs.driver_changed == 'true' || needs.paths-filter.outputs.libscap_changed == 'true'
Expand All @@ -177,28 +178,65 @@ jobs:
- name: Install deps ⛓️
run: |
sudo apt update
sudo apt install -y --no-install-recommends ca-certificates cmake build-essential clang-14 git pkg-config autoconf automake libtool libelf-dev libcap-dev
sudo apt install -y --no-install-recommends ca-certificates cmake build-essential clang-14 llvm-14 git pkg-config autoconf automake libtool libelf-dev libcap-dev linux-headers-$(uname -r)
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 90
sudo update-alternatives --install /usr/bin/llvm-strip llvm-strip /usr/bin/llvm-strip-14 90
sudo update-alternatives --install /usr/bin/llc llc /usr/bin/llc-14 90

- name: Build scap-open 🏗️
- name: Build scap-open and drivers 🏗️
run: |
mkdir -p build
cd build && cmake -DUSE_BUNDLED_DEPS=ON -DBUILD_LIBSCAP_MODERN_BPF=ON -DBUILD_MODERN_BPF_TEST=ON -DMODERN_BPF_DEBUG_MODE=ON -DBUILD_LIBSCAP_GVISOR=OFF ../
cd build && cmake -DUSE_BUNDLED_DEPS=ON -DBUILD_LIBSCAP_MODERN_BPF=ON -DBUILD_LIBSCAP_GVISOR=OFF -DBUILD_BPF=True -DCREATE_TEST_TARGETS=Off ../
make scap-open
make driver bpf

- name: Run scap-open 🏎️
- name: Run scap-open with modern bpf 🏎️
run: |
cd build
sudo ./libscap/examples/01-open/scap-open --modern_bpf --num_events 0

- name: Build bpf_test 🏗
- name: Run scap-open with bpf 🏎
run: |
cd build
make bpf_test
sudo ./libscap/examples/01-open/scap-open --bpf ./driver/bpf/probe.o --num_events 0

- name: Running tests 🧪
- name: Run scap-open with kmod 🏎️
run: |
cd build
sudo ./test/modern_bpf/bpf_test --verbose
sudo insmod ./driver/scap.ko
sudo ./libscap/examples/01-open/scap-open --kmod --num_events 0
sudo rmmod scap

build-and-test-modern-bpf-x86:
name: build-and-test-modern-bpf-x86 😇 (bundled_deps)
runs-on: ubuntu-22.04
needs: paths-filter
if: needs.paths-filter.outputs.driver_changed == 'true' || needs.paths-filter.outputs.libscap_changed == 'true'
steps:

- name: Checkout Libs ⤵️
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Install deps ⛓️
run: |
sudo apt update
sudo apt install -y --no-install-recommends ca-certificates cmake build-essential git pkg-config autoconf automake libelf-dev libcap-dev linux-headers-$(uname -r) clang-14 llvm-14 libtool
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 90
sudo update-alternatives --install /usr/bin/llvm-strip llvm-strip /usr/bin/llvm-strip-14 90
sudo update-alternatives --install /usr/bin/llc llc /usr/bin/llc-14 90

- name: Build drivers tests 🏗️
run: |
mkdir -p build
cd build && cmake -DUSE_BUNDLED_DEPS=ON -DENABLE_DRIVERS_TESTS=ON -DBUILD_LIBSCAP_MODERN_BPF=ON -DMODERN_BPF_DEBUG_MODE=ON -DBUILD_BPF=True -DBUILD_LIBSCAP_GVISOR=OFF ../
make drivers_test

- name: Run drivers_test with modern bpf 🏎️
run: |
cd build
sudo ./test/drivers/drivers_test -m

build-modern-bpf-arm64:
name: build-modern-bpf-arm64 🙃 (system_deps)
Expand Down Expand Up @@ -227,9 +265,9 @@ jobs:
git config --global --add safe.directory ${{ github.workspace }}
.github/install-deps.sh
mkdir -p build
cd build && cmake -DUSE_BUNDLED_DEPS=OFF -DBUILD_LIBSCAP_MODERN_BPF=ON -DBUILD_MODERN_BPF_TEST=ON -DMODERN_BPF_DEBUG_MODE=ON -DBUILD_LIBSCAP_GVISOR=OFF ../
cd build && cmake -DUSE_BUNDLED_DEPS=OFF -DENABLE_DRIVERS_TESTS=ON -DBUILD_LIBSCAP_MODERN_BPF=ON -DMODERN_BPF_DEBUG_MODE=ON -DBUILD_LIBSCAP_GVISOR=OFF ../
make scap-open
make bpf_test
make drivers_test

build-modern-bpf-s390x:
name: build-modern-bpf-s390x 😁 (system_deps)
Expand Down Expand Up @@ -258,9 +296,8 @@ jobs:
git config --global --add safe.directory ${{ github.workspace }}
.github/install-deps.sh
mkdir -p build
cd build && cmake -DUSE_BUNDLED_DEPS=OFF -DBUILD_LIBSCAP_MODERN_BPF=ON -DBUILD_MODERN_BPF_TEST=ON -DMODERN_BPF_DEBUG_MODE=ON -DBUILD_LIBSCAP_GVISOR=OFF ../
make scap-open
make bpf_test
cd build && cmake -DUSE_BUNDLED_DEPS=OFF -DENABLE_DRIVERS_TESTS=ON -DBUILD_LIBSCAP_MODERN_BPF=ON -DMODERN_BPF_DEBUG_MODE=ON -DBUILD_LIBSCAP_GVISOR=OFF ../
make drivers_test

build-libs-driverkit:
name: build-libs-driverkit
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ option(USE_BUNDLED_DEPS "Enable bundled dependencies instead of using the system
option(MINIMAL_BUILD "Produce a minimal build with only the essential features (no eBPF probe driver, no kubernetes, no mesos, no marathon and no container metadata)" OFF)
option(MUSL_OPTIMIZED_BUILD "Enable if you want a musl optimized build" OFF)
option(USE_BUNDLED_DRIVER "Use the driver/ subdirectory in the build process (only available in Linux)" ON)
option(ENABLE_DRIVERS_TESTS "Enable driver tests (bpf, kernel module, modern bpf)" OFF)

include(GNUInstallDirs)

Expand Down Expand Up @@ -94,4 +95,8 @@ if(CREATE_TEST_TARGETS AND NOT WIN32)
)

add_subdirectory(test/e2e)

if(ENABLE_DRIVERS_TESTS)
add_subdirectory(test/drivers)
endif()
endif()
38 changes: 17 additions & 21 deletions test/modern_bpf/CMakeLists.txt → test/drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
message(STATUS "Modern BPF tests build enabled")

include_directories(../userspace/common)
message(STATUS "Drivers tests build enabled")

## Syscall_exit suite files.
file(GLOB_RECURSE SYSCALL_EXIT_TEST_SUITE ${CMAKE_CURRENT_SOURCE_DIR}/test_suites/syscall_exit_suite/*.cpp)
Expand All @@ -11,48 +9,46 @@ file(GLOB_RECURSE SYSCALL_ENTER_TEST_SUITE ${CMAKE_CURRENT_SOURCE_DIR}/test_suit
## Generic tracepoints suite files.
file(GLOB_RECURSE GENERIC_TRACEPOINTS_TEST_SUITE ${CMAKE_CURRENT_SOURCE_DIR}/test_suites/generic_tracepoints_suite/*.cpp)

## Local suite files. (Not enabled by default)
if(BUILD_ENHANCED_MODERN_BPF_TEST)
file(GLOB_RECURSE LOCAL_TEST_SUITE ${CMAKE_CURRENT_SOURCE_DIR}/test_suites/local_suite/*.cpp)
endif()
## Actions suite files
file(GLOB_RECURSE ACTIONS_TEST_SUITE ${CMAKE_CURRENT_SOURCE_DIR}/test_suites/actions_suite/*.cpp)

set(MODERN_BPF_TEST_SOURCES
set(DRIVERS_TEST_SOURCES
./start_tests.cpp
./event_class/event_class.cpp
./flags/capabilities.cpp
./helpers/proc_parsing.cpp
"${SYSCALL_EXIT_TEST_SUITE}"
"${SYSCALL_ENTER_TEST_SUITE}"
"${GENERIC_TRACEPOINTS_TEST_SUITE}"
"${LOCAL_TEST_SUITE}"
"${ACTIONS_TEST_SUITE}"
)

set(MODERN_BPF_TEST_INCLUDE
set(DRIVERS_TEST_INCLUDE
PRIVATE
../../userspace/common
"${GTEST_INCLUDE}"
"${LIBSCAP_DIR}/driver/"
"${LIBSCAP_DIR}/userspace/libscap"
)

set(MODERN_BPF_TEST_LINK_LIBRARIES
PRIVATE
pman
scap_event_schema
set(DRIVERS_TEST_LINK_LIBRARIES
scap
"${GTEST_LIB}"
"${GTEST_MAIN_LIB}"
)

set(MODERN_BPF_TEST_DEPENDECIES
pman
set(DRIVERS_TEST_DEPENDECIES
scap
)

if(USE_BUNDLED_GTEST)
list(APPEND
MODERN_BPF_TEST_DEPENDECIES
DRIVERS_TEST_DEPENDECIES
gtest
)
endif()

add_executable(bpf_test ${MODERN_BPF_TEST_SOURCES})
target_include_directories(bpf_test ${MODERN_BPF_TEST_INCLUDE})
target_link_libraries(bpf_test ${MODERN_BPF_TEST_LINK_LIBRARIES})
add_dependencies(bpf_test ${MODERN_BPF_TEST_DEPENDECIES})
add_executable(drivers_test ${DRIVERS_TEST_SOURCES})
target_include_directories(drivers_test ${DRIVERS_TEST_INCLUDE})
target_link_libraries(drivers_test ${DRIVERS_TEST_LINK_LIBRARIES})
add_dependencies(drivers_test ${DRIVERS_TEST_DEPENDECIES})
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#include "strlcpy.h"
#include "event_class.h"
#include <time.h>
Expand Down Expand Up @@ -97,7 +96,7 @@ event_test::~event_test()
clear_ring_buffers();

/* 2 - clean all interesting syscalls. */
mark_all_64bit_syscalls_as_uninteresting();
scap_clear_ppm_sc_mask(scap_handle);
}

/* This constructor must be used with generic tracepoints
Expand Down Expand Up @@ -147,12 +146,28 @@ event_test::event_test(int syscall_id, int event_direction):
{
m_tp_set[SYS_EXIT] = 1;
m_event_type = g_syscall_table[syscall_id].exit_event_type;
if(is_bpf_engine())
{
/* The bpf engine retrieves syscall params from sys_enter tracepoints
* in kernel versions < 4.17. Moreover for syscalls that generate a
* child we need a `sched_process_fork` tracepoint to duplicate the
* syscall args for the child exit event!
*/
m_tp_set[SYS_ENTER] = 1;
if(m_event_type == PPME_SYSCALL_CLONE_20_X ||
m_event_type == PPME_SYSCALL_FORK_20_X ||
m_event_type == PPME_SYSCALL_VFORK_20_X ||
m_event_type == PPME_SYSCALL_CLONE3_X)
{
m_tp_set[SCHED_PROC_FORK] = 1;
}
}
}

m_current_param = 0;

/* Set the current as the only interesting syscall. */
mark_single_64bit_syscall_as_interesting(syscall_id);
scap_set_ppm_sc(scap_handle, g_syscall_table[syscall_id].ppm_sc, true);
}

/* This constructor must be used with syscalls events when you
Expand All @@ -167,62 +182,67 @@ event_test::event_test():
m_tp_set[SYS_ENTER] = 1;
m_tp_set[SYS_EXIT] = 1;

for(int sys_num = 0; sys_num < SYSCALL_TABLE_SIZE; sys_num++)
/* Enable all the syscalls */
for(int ppm_sc = 0; ppm_sc < PPM_SC_MAX; ppm_sc++)
{
mark_single_64bit_syscall_as_interesting(sys_num);
scap_set_ppm_sc(scap_handle, ppm_sc, true);
}
}

void event_test::mark_single_64bit_syscall_as_interesting(int interesting_syscall_id)
{
pman_mark_single_64bit_syscall(interesting_syscall_id, true);
}

void event_test::mark_all_64bit_syscalls_as_uninteresting()
{
pman_clean_all_64bit_interesting_syscalls();
}

void event_test::enable_capture()
{
pman_enable_capture((bool*)m_tp_set.data());
/* Here I should enable the necessary tracepoints */
for(int i = 0; i < TP_VAL_MAX; i++)
{
if(m_tp_set[i])
{
scap_set_tpmask(scap_handle, i, true);
}
}
/* We need to clear all the `ring-buffers` because maybe during
* the tracepoint attachment we triggered some syscalls
*/
clear_ring_buffers();
}

void event_test::disable_capture()
{
pman_disable_capture();
scap_stop_capture(scap_handle);
}

void event_test::clear_ring_buffers()
{
int16_t cpu_id = 0;
while(get_event_from_ringbuffer(&cpu_id) != NULL)
uint16_t cpu_id = 0;
/* First timeout means that all the buffers are empty. If the capture is not
* stopped it is possible that we will never receive a `SCAP_TIMEOUT`.
*/
while(scap_next(scap_handle, (scap_evt**)&m_event_header, &cpu_id) != SCAP_TIMEOUT)
{
};
}

bool event_test::are_all_ringbuffers_full(unsigned long threshold)
{
return pman_are_all_ringbuffers_full(threshold);
}
}

struct ppm_evt_hdr* event_test::get_event_from_ringbuffer(int16_t* cpu_id)
void event_test::get_event_from_ringbuffer(uint16_t* cpu_id)
{
/* Clear acutal event */
m_event_header = NULL;
uint16_t attempts = 0;
int32_t res = 0;

/* Try 2 times just to be sure that all the buffers are empty. */
while(attempts <= 1)
{
pman_consume_first_from_buffers((void**)&m_event_header, cpu_id);
if(m_event_header != NULL)
res = scap_next(scap_handle, (scap_evt**)&m_event_header, cpu_id);
if(res == SCAP_SUCCESS && m_event_header != NULL)
{
return m_event_header;
return;
}
else if(res != SCAP_TIMEOUT && res != SCAP_SUCCESS)
{
FAIL() << "Unexpected error value from scap-next: " << res << std::endl;
}
attempts++;
}
return m_event_header;
return;
}

void event_test::parse_event()
Expand Down Expand Up @@ -419,7 +439,8 @@ void event_test::assert_event_absence(pid_t pid_to_search, int event_to_search)

void event_test::assert_header()
{
int num_params_from_bpf_table = pman_get_event_params(m_event_type);
/* TODO: Here we need a `scap` function that exposes some fields of the table and not all the table!! */
int num_params_from_bpf_table = scap_get_event_info_table()[m_event_type].nparams;

/* the bpf event gets the correct number of parameters from the param table. */
ASSERT_EQ(m_event_header->nparams, num_params_from_bpf_table) << "'nparams' in the header is not correct." << std::endl;
Expand All @@ -429,7 +450,8 @@ void event_test::assert_header()

void event_test::assert_num_params_pushed(int total_params)
{
int num_params_from_bpf_table = pman_get_event_params(m_event_type);
/* TODO: Here we need a `scap` function that exposes some fields of the table and not all the table!! */
int num_params_from_bpf_table = scap_get_event_info_table()[m_event_type].nparams;
ASSERT_EQ(total_params, num_params_from_bpf_table) << "for this event we have not pushed the right number of parameters." << std::endl;
}

Expand Down Expand Up @@ -537,7 +559,16 @@ void event_test::assert_cgroup_param(int param_num)
strlcpy(cgroup_prefix, cgroup_string, prefix_len + 1);
ASSERT_STREQ(cgroup_prefix, cgroup_prefix_array[index]) << VALUE_NOT_CORRECT << m_current_param;
}
assert_param_len(total_len);

/* With the kmod we send more cgroups than the 5 we send in bpf and modern bpf */
if(is_kmod_engine())
{
assert_param_len_ge(total_len);
}
else
{
assert_param_len(total_len);
}
}

void event_test::assert_bytebuf_param(int param_num, const char* param, int buf_dimension)
Expand Down Expand Up @@ -754,6 +785,12 @@ void event_test::assert_param_len(uint16_t expected_size)
ASSERT_EQ(size, expected_size) << ">>>>> length of the param is not correct. Param id = " << m_current_param << std::endl;
}

void event_test::assert_param_len_ge(uint16_t expected_size)
{
uint16_t size = m_event_params[m_current_param].len;
ASSERT_GE(size, expected_size) << ">>>>> length of the param is not correct. Param id = " << m_current_param << std::endl;
}

void event_test::assert_address_family(uint8_t desired_family, int starting_index)
{
uint8_t family = (uint8_t)(m_event_params[m_current_param].valptr[starting_index]);
Expand Down Expand Up @@ -819,7 +856,7 @@ void event_test::assert_unix_path(const char* desired_path, int starting_index)

void event_test::assert_event_in_buffers(pid_t pid_to_search, int event_to_search, bool presence)
{
int16_t cpu_id = 0;
uint16_t cpu_id = 0;
pid_t pid = 0;
uint16_t evt_type = 0;

Expand Down
Loading