From 18de8ce6d714b2fd8ba07129b61e2b3888ba8bba Mon Sep 17 00:00:00 2001 From: Andrea Terzolo Date: Wed, 7 Aug 2024 18:09:36 +0200 Subject: [PATCH] test: add tests for the new per-CPU metrics Signed-off-by: Andrea Terzolo --- test/libscap/test_suites/engines/bpf/bpf.cpp | 54 +++++++++++++++++++ .../libscap/test_suites/engines/kmod/kmod.cpp | 54 +++++++++++++++++++ .../engines/modern_bpf/modern_bpf.cpp | 54 +++++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/test/libscap/test_suites/engines/bpf/bpf.cpp b/test/libscap/test_suites/engines/bpf/bpf.cpp index 60439298cb..e68befc6b9 100644 --- a/test/libscap/test_suites/engines/bpf/bpf.cpp +++ b/test/libscap/test_suites/engines/bpf/bpf.cpp @@ -129,6 +129,60 @@ TEST(bpf, double_scap_stats_call) scap_close(h); } +TEST(bpf, metrics_v2_check_per_CPU_stats) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + scap_t* h = open_bpf_engine(error_buffer, &ret, 4 * 4096, LIBSCAP_TEST_BPF_PROBE_PATH); + ASSERT_FALSE(!h || ret != SCAP_SUCCESS) << "unable to open bpf engine: " << error_buffer << std::endl; + + ssize_t num_possible_CPUs = num_possible_cpus(); + + // We want to check our CPUs counters + uint32_t flags = METRICS_V2_KERNEL_COUNTERS; + uint32_t nstats = 0; + int32_t rc = 0; + const metrics_v2* stats_v2 = scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_TRUE(stats_v2); + ASSERT_GT(nstats, 0); + + uint32_t i = 0; + ssize_t found = 0; + char expected_name[METRIC_NAME_MAX] = ""; + snprintf(expected_name, METRIC_NAME_MAX, N_EVENTS_PER_CPU_PREFIX"%ld", found); + + while(i < nstats) + { + // `sizeof(N_EVENTS_PER_CPU_PREFIX)-1` because we need to exclude the `\0` + if(strncmp(stats_v2[i].name, N_EVENTS_PER_CPU_PREFIX, sizeof(N_EVENTS_PER_CPU_PREFIX)-1) == 0) + { + i++; + // The next metric should be the number of drops + snprintf(expected_name, METRIC_NAME_MAX, N_DROPS_PER_CPU_PREFIX"%ld", found); + if(strncmp(stats_v2[i].name, N_DROPS_PER_CPU_PREFIX, sizeof(N_DROPS_PER_CPU_PREFIX)-1) == 0) + { + i++; + found++; + } + else + { + FAIL() << "Missing CPU drops for CPU " << found; + } + } + else + { + i++; + } + } + + // This test could fail in case of rare race conditions in which the number of available CPUs changes + // between the scap_open and the `num_possible_cpus` function. In CI we shouldn't have hot plugs so probably we + // can live with this. + ASSERT_EQ(num_possible_CPUs, found) << "We didn't find the stats for all the CPUs"; + scap_close(h); +} + TEST(bpf, metrics_v2_check_results) { char error_buffer[SCAP_LASTERR_SIZE] = {0}; diff --git a/test/libscap/test_suites/engines/kmod/kmod.cpp b/test/libscap/test_suites/engines/kmod/kmod.cpp index 8518111ff7..e7e1933371 100644 --- a/test/libscap/test_suites/engines/kmod/kmod.cpp +++ b/test/libscap/test_suites/engines/kmod/kmod.cpp @@ -184,6 +184,60 @@ TEST(kmod, double_scap_stats_call) scap_close(h); } +TEST(kmod, metrics_v2_check_per_CPU_stats) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + scap_t* h = open_kmod_engine(error_buffer, &ret, 4 * 4096, LIBSCAP_TEST_KERNEL_MODULE_PATH); + ASSERT_FALSE(!h || ret != SCAP_SUCCESS) << "unable to open kmod engine: " << error_buffer << std::endl; + + ssize_t num_online_CPUs = sysconf(_SC_NPROCESSORS_ONLN); + + // We want to check our CPUs counters + uint32_t flags = METRICS_V2_KERNEL_COUNTERS; + uint32_t nstats = 0; + int32_t rc = 0; + const metrics_v2* stats_v2 = scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_TRUE(stats_v2); + ASSERT_GT(nstats, 0); + + uint32_t i = 0; + ssize_t found = 0; + char expected_name[METRIC_NAME_MAX] = ""; + snprintf(expected_name, METRIC_NAME_MAX, N_EVENTS_PER_DEVICE_PREFIX"%ld", found); + + while(i < nstats) + { + // `sizeof(N_EVENTS_PER_DEVICE_PREFIX)-1` because we need to exclude the `\0` + if(strncmp(stats_v2[i].name, N_EVENTS_PER_DEVICE_PREFIX, sizeof(N_EVENTS_PER_DEVICE_PREFIX)-1) == 0) + { + i++; + // The next metric should be the number of drops + snprintf(expected_name, METRIC_NAME_MAX, N_DROPS_PER_DEVICE_PREFIX"%ld", found); + if(strncmp(stats_v2[i].name, N_DROPS_PER_DEVICE_PREFIX, sizeof(N_DROPS_PER_DEVICE_PREFIX)-1) == 0) + { + i++; + found++; + } + else + { + FAIL() << "Missing CPU drops for CPU " << found; + } + } + else + { + i++; + } + } + + // This test could fail in case of rare race conditions in which the number of online CPUs changes + // between the scap_open and the `sysconf(_SC_NPROCESSORS_ONLN)` function. In CI we shouldn't have hot plugs so probably we + // can live with this. + ASSERT_EQ(num_online_CPUs, found) << "We didn't find the stats for all the CPUs"; + scap_close(h); +} + TEST(kmod, metrics_v2_check_results) { char error_buffer[SCAP_LASTERR_SIZE] = {0}; diff --git a/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp b/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp index f59a976c23..267f0b7a0f 100644 --- a/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp +++ b/test/libscap/test_suites/engines/modern_bpf/modern_bpf.cpp @@ -248,6 +248,60 @@ TEST(modern_bpf, double_scap_stats_call) scap_close(h); } +TEST(modern_bpf, metrics_v2_check_per_CPU_stats) +{ + char error_buffer[FILENAME_MAX] = {0}; + int ret = 0; + scap_t* h = open_modern_bpf_engine(error_buffer, &ret, 1 * 1024 * 1024, 0, false); + ASSERT_EQ(!h || ret != SCAP_SUCCESS, false) << "unable to open modern bpf engine with one single shared ring buffer: " << error_buffer << std::endl; + + ssize_t num_possible_CPUs = num_possible_cpus(); + + // We want to check our CPUs counters + uint32_t flags = METRICS_V2_KERNEL_COUNTERS; + uint32_t nstats = 0; + int32_t rc = 0; + const metrics_v2* stats_v2 = scap_get_stats_v2(h, flags, &nstats, &rc); + ASSERT_EQ(rc, SCAP_SUCCESS); + ASSERT_TRUE(stats_v2); + ASSERT_GT(nstats, 0); + + uint32_t i = 0; + ssize_t found = 0; + char expected_name[METRIC_NAME_MAX] = ""; + snprintf(expected_name, METRIC_NAME_MAX, N_EVENTS_PER_CPU_PREFIX"%ld", found); + + while(i < nstats) + { + // `sizeof(N_EVENTS_PER_CPU_PREFIX)-1` because we need to exclude the `\0` + if(strncmp(stats_v2[i].name, N_EVENTS_PER_CPU_PREFIX, sizeof(N_EVENTS_PER_CPU_PREFIX)-1) == 0) + { + i++; + // The next metric should be the number of drops + snprintf(expected_name, METRIC_NAME_MAX, N_DROPS_PER_CPU_PREFIX"%ld", found); + if(strncmp(stats_v2[i].name, N_DROPS_PER_CPU_PREFIX, sizeof(N_DROPS_PER_CPU_PREFIX)-1) == 0) + { + i++; + found++; + } + else + { + FAIL() << "Missing CPU drops for CPU " << found; + } + } + else + { + i++; + } + } + + // This test could fail in case of rare race conditions in which the number of available CPUs changes + // between the scap_open and the `num_possible_cpus` function. In CI we shouldn't have hot plugs so probably we + // can live with this. + ASSERT_EQ(num_possible_CPUs, found) << "We didn't find the stats for all the CPUs"; + scap_close(h); +} + TEST(modern_bpf, metrics_v2_check_results) { char error_buffer[FILENAME_MAX] = {0};