stress test for tail calls (#2518)
* Add stress test for tail calls * Added the code generated bindmonitor_mt_tailcall files * Added the test case description to readme.md * Update tests/stress/km/stress_tests_km.cpp Co-authored-by: Dave Thaler <dthaler@microsoft.com> * Update tests/stress/km/stress_tests_km.cpp Co-authored-by: Dave Thaler <dthaler@microsoft.com> * Addressed PR comments and Fixed merge conflicts with latest * Fixed function name * Regenerated the expected files, after the macro MAX TAIL CNT change done yesterday * Update tests/stress/readme.md Co-authored-by: Dave Thaler <dthaler@microsoft.com> * Removed clean_up tail call * Add bind in loop * updated the readme * Addressed PR comments * Changed LOG_VERBOSE to LOG_ERROR * Updated readme and addressed PR comment --------- Co-authored-by: Dave Thaler <dthaler@microsoft.com>
This commit is contained in:
Родитель
8dead3834e
Коммит
8adab0de10
|
@ -650,6 +650,18 @@ SPDX-License-Identifier: MIT
|
|||
<Component Id="BINDMONITOR_RINGBUF_UM.PDB" DiskId="1" Guid="{FB8688DC-43FB-475A-ADB5-B669A97A435C}">
|
||||
<File Id="BINDMONITOR_RINGBUF_UM.PDB" Name="bindmonitor_ringbuf_um.pdb" Source="$(var.SolutionDir)$(var.Platform)\$(var.Configuration)\bindmonitor_ringbuf_um.pdb" />
|
||||
</Component>
|
||||
<Component Id="BINDMONITOR_MT_TAILCALL.O" DiskId="1" Guid="{5AF0DDDA-205B-440D-9F2F-B3554A22F2B1}">
|
||||
<File Id="BINDMONITOR_MT_TAILCALL.O" Name="bindmonitor_mt_tailcall.o" Source="$(var.SolutionDir)$(var.Platform)\$(var.Configuration)\bindmonitor_mt_tailcall.o" />
|
||||
</Component>
|
||||
<Component Id="BINDMONITOR_MT_TAILCALL.SYS" DiskId="1" Guid="{BAFF9117-CF1D-40DD-8B4A-5B72F181DB99}">
|
||||
<File Id="BINDMONITOR_MT_TAILCALL.SYS" Name="bindmonitor_mt_tailcall.sys" Source="$(var.SolutionDir)$(var.Platform)\$(var.Configuration)\bindmonitor_mt_tailcall.sys" />
|
||||
</Component>
|
||||
<Component Id="BINDMONITOR_MT_TAILCALL_UM.DLL" DiskId="1" Guid="{72FEF59C-A7D8-4BB5-B11E-5A125073723E}">
|
||||
<File Id="BINDMONITOR_MT_TAILCALL_UM.DLL" Name="bindmonitor_mt_tailcall_um.dll" Source="$(var.SolutionDir)$(var.Platform)\$(var.Configuration)\bindmonitor_mt_tailcall_um.dll" />
|
||||
</Component>
|
||||
<Component Id="BINDMONITOR_MT_TAILCALL_UM.PDB" DiskId="1" Guid="{70AD50CC-C376-4FD4-900E-1157F9A74A12}">
|
||||
<File Id="BINDMONITOR_MT_TAILCALL_UM.PDB" Name="bindmonitor_mt_tailcall_um.pdb" Source="$(var.SolutionDir)$(var.Platform)\$(var.Configuration)\bindmonitor_mt_tailcall_um.pdb" />
|
||||
</Component>
|
||||
<Component Id="BINDMONITOR_TAILCALL.O" DiskId="1" Guid="{6A37AE43-179A-46BD-97D5-3D02B6807765}">
|
||||
<File Id="BINDMONITOR_TAILCALL.O" Name="bindmonitor_tailcall.o" Source="$(var.SolutionDir)$(var.Platform)\$(var.Configuration)\bindmonitor_tailcall.o" />
|
||||
</Component>
|
||||
|
|
|
@ -48,6 +48,10 @@ $source_directory="."
|
|||
"bindmonitor_ringbuf_um.dll",
|
||||
"bindmonitor_ringbuf_um.pdb",
|
||||
"bindmonitor_ringbuf.sys",
|
||||
"bindmonitor_mt_tailcall.o",
|
||||
"bindmonitor_mt_tailcall_um.dll",
|
||||
"bindmonitor_mt_tailcall_um.pdb",
|
||||
"bindmonitor_mt_tailcall.sys",
|
||||
"bindmonitor_tailcall.o",
|
||||
"bindmonitor_tailcall_um.dll",
|
||||
"bindmonitor_tailcall_um.pdb",
|
||||
|
|
|
@ -190,6 +190,7 @@ DECLARE_TEST("bad_map_name", _test_mode::Verify)
|
|||
DECLARE_TEST("bindmonitor", _test_mode::Verify)
|
||||
DECLARE_TEST("bindmonitor_ringbuf", _test_mode::Verify)
|
||||
DECLARE_TEST("bindmonitor_tailcall", _test_mode::Verify)
|
||||
DECLARE_TEST("bindmonitor_mt_tailcall", _test_mode::Verify)
|
||||
DECLARE_TEST_CUSTOM_PROGRAM_TYPE("bpf", _test_mode::Verify, std::string("xdp"))
|
||||
DECLARE_TEST("bpf_call", _test_mode::Verify)
|
||||
DECLARE_TEST("cgroup_sock_addr", _test_mode::Verify)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) Microsoft Corporation
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// clang -O2 -Werror -c bindmonitor_mt_tailcall.c -o bindmonitor_mt_tailcall_jit.o
|
||||
//
|
||||
// For bpf code: clang -target bpf -O2 -Werror -c bindmonitor_mt_tailcall.c -o bindmonitor_mt_tailcall.o
|
||||
// this passes the checker
|
||||
|
||||
// Whenever this sample program changes, bpf2c_tests will fail unless the
|
||||
// expected files in tests\bpf2c_tests\expected are updated. The following
|
||||
// script can be used to regenerate the expected files:
|
||||
// generate_expected_bpf2c_output.ps1
|
||||
//
|
||||
// Usage:
|
||||
// .\scripts\generate_expected_bpf2c_output.ps1 <build_output_path>
|
||||
// Example:
|
||||
// .\scripts\generate_expected_bpf2c_output.ps1 .\x64\Debug\
|
||||
|
||||
#include "bpf_helpers.h"
|
||||
#include "ebpf_nethooks.h"
|
||||
|
||||
SEC("maps")
|
||||
struct bpf_map_def bind_tail_call_map = {
|
||||
.type = BPF_MAP_TYPE_PROG_ARRAY,
|
||||
.key_size = sizeof(uint32_t),
|
||||
.value_size = sizeof(uint32_t),
|
||||
.max_entries = MAX_TAIL_CALL_CNT};
|
||||
|
||||
SEC("bind")
|
||||
bind_action_t
|
||||
BindMonitor_Caller(bind_md_t* ctx)
|
||||
{
|
||||
bpf_printk("Tail call index %d\n", 0);
|
||||
bpf_tail_call(ctx, &bind_tail_call_map, 0);
|
||||
|
||||
return BIND_DENY;
|
||||
}
|
||||
|
||||
// Define a macro that defines a program which tail calls a function for the bind hook.
|
||||
#define DEFINE_BIND_TAIL_FUNC(x) \
|
||||
SEC("bind/" #x) \
|
||||
bind_action_t BindMonitor_Callee##x(bind_md_t* ctx) \
|
||||
{ \
|
||||
bpf_printk("Tail call index %d\n", x + 1); \
|
||||
bpf_tail_call(ctx, &bind_tail_call_map, x + 1); \
|
||||
\
|
||||
return BIND_DENY; \
|
||||
}
|
||||
|
||||
DEFINE_BIND_TAIL_FUNC(0)
|
||||
DEFINE_BIND_TAIL_FUNC(1)
|
||||
DEFINE_BIND_TAIL_FUNC(2)
|
||||
DEFINE_BIND_TAIL_FUNC(3)
|
||||
DEFINE_BIND_TAIL_FUNC(4)
|
||||
DEFINE_BIND_TAIL_FUNC(5)
|
||||
DEFINE_BIND_TAIL_FUNC(6)
|
||||
DEFINE_BIND_TAIL_FUNC(7)
|
||||
DEFINE_BIND_TAIL_FUNC(8)
|
||||
DEFINE_BIND_TAIL_FUNC(9)
|
||||
DEFINE_BIND_TAIL_FUNC(10)
|
||||
DEFINE_BIND_TAIL_FUNC(11)
|
||||
DEFINE_BIND_TAIL_FUNC(12)
|
||||
DEFINE_BIND_TAIL_FUNC(13)
|
||||
DEFINE_BIND_TAIL_FUNC(14)
|
||||
DEFINE_BIND_TAIL_FUNC(15)
|
||||
DEFINE_BIND_TAIL_FUNC(16)
|
||||
DEFINE_BIND_TAIL_FUNC(17)
|
||||
DEFINE_BIND_TAIL_FUNC(18)
|
||||
DEFINE_BIND_TAIL_FUNC(19)
|
||||
DEFINE_BIND_TAIL_FUNC(20)
|
||||
DEFINE_BIND_TAIL_FUNC(21)
|
||||
DEFINE_BIND_TAIL_FUNC(22)
|
||||
DEFINE_BIND_TAIL_FUNC(23)
|
||||
DEFINE_BIND_TAIL_FUNC(24)
|
||||
DEFINE_BIND_TAIL_FUNC(25)
|
||||
DEFINE_BIND_TAIL_FUNC(26)
|
||||
DEFINE_BIND_TAIL_FUNC(27)
|
||||
DEFINE_BIND_TAIL_FUNC(28)
|
||||
DEFINE_BIND_TAIL_FUNC(29)
|
||||
DEFINE_BIND_TAIL_FUNC(30)
|
||||
|
||||
// This line verifies that the BindMonitor_Caller prototype is correct by declaring a bind_hook_t
|
||||
// variable with the same name as the first tail call function.
|
||||
// This line is optional.
|
||||
bind_hook_t BindMonitor_Caller;
|
||||
|
||||
SEC("bind/31")
|
||||
bind_action_t
|
||||
BindMonitor_Callee31(bind_md_t* ctx)
|
||||
{
|
||||
// This function is the last tail call function for the bind hook.
|
||||
// This function returns BIND_PERMIT to allow the bind request to proceed.
|
||||
return BIND_PERMIT;
|
||||
}
|
|
@ -13,6 +13,13 @@
|
|||
#include "socket_helper.h"
|
||||
#include "socket_tests_common.h"
|
||||
|
||||
// The helper function definitions in bpf_helpers.h conflict with the definitions
|
||||
// in libbpf.h, but this code needs to use MAX_TAIL_CALL_CNT from bpf_helpers.h.
|
||||
// Work around this by defining MAX_TAIL_CALL_CNT here.
|
||||
#if !defined(MAX_TAIL_CALL_CNT)
|
||||
#define MAX_TAIL_CALL_CNT 32
|
||||
#endif
|
||||
|
||||
// Note: The 'program' and 'execution' types are not required for km tests.
|
||||
static const std::map<std::string, test_program_attributes> _test_program_info = {
|
||||
{{"cgroup_sock_addr"},
|
||||
|
@ -1084,6 +1091,305 @@ _print_test_control_info(const test_control_info& test_control_info)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_set_up_tailcall_program(bpf_object* object, const std::string& map_name)
|
||||
{
|
||||
REQUIRE(object != nullptr);
|
||||
|
||||
fd_t prog_map_fd = bpf_object__find_map_fd_by_name(object, map_name.c_str());
|
||||
if (prog_map_fd < 0) {
|
||||
LOG_ERROR(
|
||||
"({}) FATAL ERROR: bpf_object__find_map_fd_by_name({}) failed. Errno:{}",
|
||||
__func__,
|
||||
map_name.c_str(),
|
||||
errno);
|
||||
REQUIRE(prog_map_fd >= 0);
|
||||
}
|
||||
LOG_VERBOSE("({}) Opened fd:{} for map:{}", __func__, prog_map_fd, map_name.c_str());
|
||||
|
||||
// Set up tail calls.
|
||||
for (int index = 0; index < MAX_TAIL_CALL_CNT; index++) {
|
||||
try {
|
||||
std::string bind_program_name{"BindMonitor_Callee"};
|
||||
bind_program_name += std::to_string(index);
|
||||
|
||||
// Try to get a handle to the 'BindMonitor_Callee<index>' program.
|
||||
bpf_program* callee = bpf_object__find_program_by_name(object, bind_program_name.c_str());
|
||||
|
||||
if (callee == nullptr) {
|
||||
LOG_ERROR("({}) - bpf_object__find_program_by_name() failed. errno: {}", bind_program_name, errno);
|
||||
REQUIRE(callee != nullptr);
|
||||
}
|
||||
LOG_VERBOSE("({}) - bpf_object__find_program_by_name() success.", bind_program_name);
|
||||
|
||||
fd_t callee_fd = bpf_program__fd(callee);
|
||||
if (callee_fd < 0) {
|
||||
LOG_ERROR("({}) - bpf_program__fd() failed. errno: {}", bind_program_name, errno);
|
||||
REQUIRE(callee_fd > 0);
|
||||
}
|
||||
LOG_VERBOSE("({}) - bpf_program__fd() for callee_fd:{} success.", bind_program_name, callee_fd);
|
||||
|
||||
uint32_t result = bpf_map_update_elem(prog_map_fd, &index, &callee_fd, 0);
|
||||
if (result < 0) {
|
||||
LOG_ERROR("({}) - bpf_map_update_elem() failed. errno: {}", bind_program_name, errno);
|
||||
REQUIRE(result == ERROR_SUCCESS);
|
||||
}
|
||||
LOG_VERBOSE("({}) - bpf_map_update_elem() for callee_fd:{} success.", bind_program_name, callee_fd);
|
||||
|
||||
} catch (...) {
|
||||
// No need to terminate. We don't care about user mode issues here.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_invoke_mt_bindmonitor_tail_call_thread_function(thread_context& context)
|
||||
{
|
||||
// Test bind.
|
||||
SOCKET socket_handle = INVALID_SOCKET;
|
||||
SOCKADDR_STORAGE remote_endpoint{};
|
||||
|
||||
if (context.role == thread_role_type::MONITOR_IPV4) {
|
||||
remote_endpoint.ss_family = AF_INET;
|
||||
} else {
|
||||
ASSERT(context.role == thread_role_type::MONITOR_IPV6);
|
||||
remote_endpoint.ss_family = AF_INET6;
|
||||
}
|
||||
|
||||
uint16_t remote_port = SOCKET_TEST_PORT + static_cast<uint16_t>(context.thread_index);
|
||||
using sc = std::chrono::steady_clock;
|
||||
auto endtime = sc::now() + std::chrono::minutes(context.duration_minutes);
|
||||
while (sc::now() < endtime) {
|
||||
|
||||
// Now send out a small burst of TCP 'bind' attempts for the duration of the test. We do this to increase
|
||||
// the probability of a collision between the program invocation and the extension being restarted at the same
|
||||
// time.
|
||||
constexpr uint32_t BURST_SIZE = 5;
|
||||
int result = 0;
|
||||
|
||||
for (uint32_t i = 0; i < BURST_SIZE; i++) {
|
||||
// Create a socket.
|
||||
socket_handle = WSASocket(remote_endpoint.ss_family, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, 0);
|
||||
REQUIRE(socket_handle != INVALID_SOCKET);
|
||||
|
||||
INETADDR_SETANY(reinterpret_cast<PSOCKADDR>(&remote_endpoint));
|
||||
SS_PORT(&remote_endpoint) = htons(remote_port);
|
||||
|
||||
// Forcefully bind to the same port in use using socket option SO_REUSEADDR.
|
||||
// One of the use-case: multicast sockets.
|
||||
const char optval = 1;
|
||||
result = setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
|
||||
if (result != 0) {
|
||||
LOG_ERROR(
|
||||
"Thread[{}] setsockopt result:{} {} to port:{}",
|
||||
context.thread_index,
|
||||
result,
|
||||
WSAGetLastError(),
|
||||
remote_port);
|
||||
closesocket(socket_handle);
|
||||
REQUIRE(result == 0);
|
||||
}
|
||||
|
||||
// Bind the socket.
|
||||
LOG_VERBOSE("Thread[{}] - binding to port:{}", context.thread_index, remote_port);
|
||||
result = bind(socket_handle, (PSOCKADDR)&remote_endpoint, sizeof(remote_endpoint));
|
||||
if (result != 0) {
|
||||
LOG_ERROR(
|
||||
"Thread[{}] bind result:{} {} to port:{}",
|
||||
context.thread_index,
|
||||
result,
|
||||
WSAGetLastError(),
|
||||
remote_port);
|
||||
closesocket(socket_handle);
|
||||
REQUIRE(result == 0);
|
||||
}
|
||||
|
||||
LOG_VERBOSE("Thread[{}] bind success to port:{}", context.thread_index, remote_port);
|
||||
closesocket(socket_handle);
|
||||
}
|
||||
}
|
||||
LOG_VERBOSE("Thread[{}] Done.", context.thread_index);
|
||||
}
|
||||
|
||||
static std::pair<bpf_object_ptr, fd_t>
|
||||
_load_attach_tail_program(
|
||||
const std::string& file_name,
|
||||
uint32_t thread_index,
|
||||
ebpf_attach_type_t attach_type,
|
||||
const std::string program_name,
|
||||
bpf_prog_type program_type)
|
||||
{
|
||||
bpf_object_ptr object_ptr;
|
||||
bpf_object* object_raw_ptr = nullptr;
|
||||
bpf_link* link = nullptr;
|
||||
|
||||
// Get the 'object' ptr for the program associated with this thread.
|
||||
object_raw_ptr = bpf_object__open(file_name.c_str());
|
||||
if (object_raw_ptr == nullptr) {
|
||||
LOG_ERROR(
|
||||
"{}({}) FATAL ERROR: bpf_object__open({}) failed. errno:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
file_name.c_str(),
|
||||
errno);
|
||||
REQUIRE(object_raw_ptr != nullptr);
|
||||
}
|
||||
LOG_VERBOSE("{}({}) Opened file:{}", __func__, thread_index, file_name.c_str());
|
||||
|
||||
// Load the program.
|
||||
auto result = bpf_object__load(object_raw_ptr);
|
||||
if (result != 0) {
|
||||
LOG_ERROR(
|
||||
"{}({}) FATAL ERROR: bpf_object__load({}) failed. errno:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
file_name.c_str(),
|
||||
errno);
|
||||
REQUIRE(result == 0);
|
||||
}
|
||||
object_ptr.reset(object_raw_ptr);
|
||||
LOG_VERBOSE("{}({}) loaded file:{}", __func__, thread_index, file_name.c_str());
|
||||
|
||||
// Load program by name.
|
||||
bpf_program* program = bpf_object__find_program_by_name(object_raw_ptr, program_name.c_str());
|
||||
if (program == nullptr) {
|
||||
LOG_ERROR(
|
||||
"{}({}) FATAL ERROR: bpf_object__find_program_by_name({}) failed. errno:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
file_name.c_str(),
|
||||
errno);
|
||||
REQUIRE(program != nullptr);
|
||||
}
|
||||
LOG_VERBOSE(
|
||||
"{}({}) Found program object for program:{}, file_name:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
program->program_name,
|
||||
file_name.c_str());
|
||||
|
||||
// Set the program type.
|
||||
bpf_program__set_type(program, program_type);
|
||||
|
||||
// Get the fd for this program.
|
||||
fd_t program_fd = bpf_program__fd(program);
|
||||
if (program_fd < 0) {
|
||||
LOG_ERROR(
|
||||
"{}({}) FATAL ERROR: bpf_program__fd({}) failed. program:{}, errno:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
file_name.c_str(),
|
||||
program->program_name,
|
||||
errno);
|
||||
REQUIRE(program_fd >= 0);
|
||||
}
|
||||
LOG_VERBOSE(
|
||||
"{}({}) Opened fd:{}, for program:{}, file_name:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
program_fd,
|
||||
program->program_name,
|
||||
file_name.c_str());
|
||||
|
||||
result = ebpf_program_attach(program, &attach_type, nullptr, 0, &link);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
LOG_ERROR(
|
||||
"{}({}) FATAL ERROR: bpf_prog_attach({}) failed. program:{}, errno:{}",
|
||||
__func__,
|
||||
thread_index,
|
||||
file_name.c_str(),
|
||||
program->program_name,
|
||||
errno);
|
||||
REQUIRE(result == ERROR_SUCCESS);
|
||||
}
|
||||
LOG_VERBOSE(
|
||||
"{}({}) Attached program:{}, file_name:{}", __func__, thread_index, program->program_name, file_name.c_str());
|
||||
|
||||
return std::make_pair(std::move(object_ptr), program_fd);
|
||||
}
|
||||
|
||||
static void
|
||||
_mt_bindmonitor_tail_call_invoke_program_test(const test_control_info& test_control_info)
|
||||
{
|
||||
WSAData data{};
|
||||
auto error = WSAStartup(MAKEWORD(2, 2), &data);
|
||||
REQUIRE(error == 0);
|
||||
|
||||
std::string file_name = "bindmonitor_mt_tailcall.sys";
|
||||
std::string map_name = "bind_tail_call_map";
|
||||
std::string program_name = "BindMonitor_Caller";
|
||||
bpf_prog_type program_type = BPF_PROG_TYPE_BIND;
|
||||
|
||||
// Load the program.
|
||||
auto [program_object, _] =
|
||||
_load_attach_tail_program(file_name, 0, EBPF_ATTACH_TYPE_BIND, program_name, program_type);
|
||||
|
||||
// Set up the tail call programs.
|
||||
_set_up_tailcall_program(program_object.get(), map_name);
|
||||
|
||||
// Needed for thread_context initialization.
|
||||
constexpr uint32_t MAX_BIND_PROGRAM = 1;
|
||||
|
||||
// Storage for object pointers for each ebpf program file opened by bpf_object__open().
|
||||
std::vector<object_table_entry> object_table(MAX_BIND_PROGRAM);
|
||||
for (uint32_t index = 0; auto& entry : object_table) {
|
||||
entry.available = true;
|
||||
entry.lock = std::make_unique<std::mutex>();
|
||||
entry.object = std::move(program_object);
|
||||
entry.attach = !(index % 2) ? true : false;
|
||||
entry.index = index++;
|
||||
entry.reuse_count = 0;
|
||||
entry.tag = 0xC001DEA2;
|
||||
}
|
||||
|
||||
size_t total_threads = test_control_info.threads_count;
|
||||
std::vector<thread_context> thread_context_table(
|
||||
total_threads, {{}, {}, false, {}, thread_role_type::ROLE_NOT_SET, 0, 0, 0, false, 0, 0, object_table});
|
||||
std::vector<std::thread> test_thread_table(total_threads);
|
||||
for (uint32_t i = 0; i < total_threads; i++) {
|
||||
|
||||
// First, prepare the context for this thread.
|
||||
auto& context_entry = thread_context_table[i];
|
||||
context_entry.is_native_program = true;
|
||||
context_entry.role = thread_role_type::MONITOR_IPV6;
|
||||
context_entry.thread_index = i;
|
||||
context_entry.duration_minutes = test_control_info.duration_minutes;
|
||||
context_entry.extension_restart_enabled = test_control_info.extension_restart_enabled;
|
||||
|
||||
// Now create the thread.
|
||||
auto& thread_entry = test_thread_table[i];
|
||||
thread_entry =
|
||||
std::move(std::thread(_invoke_mt_bindmonitor_tail_call_thread_function, std::ref(context_entry)));
|
||||
}
|
||||
|
||||
// Another table for the 'extension restart' threads.
|
||||
std::vector<std::thread> extension_restart_thread_table{};
|
||||
|
||||
// If requested, start the 'extension stop-and-restart' thread for extension for this program type.
|
||||
std::string extension_name = {"netebpfext"};
|
||||
if (test_control_info.extension_restart_enabled) {
|
||||
auto restart_thread = _start_extension_restart_thread(
|
||||
std::ref(extension_name), test_control_info.extension_restart_delay_ms, test_control_info.duration_minutes);
|
||||
extension_restart_thread_table.push_back(std::move(restart_thread));
|
||||
}
|
||||
|
||||
// Wait for threads to terminate.
|
||||
LOG_INFO("waiting on {} test threads...", test_thread_table.size());
|
||||
for (auto& t : test_thread_table) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
if (test_control_info.extension_restart_enabled) {
|
||||
LOG_INFO("waiting on {} extension restart threads...", extension_restart_thread_table.size());
|
||||
for (auto& t : extension_restart_thread_table) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up Winsock.
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
TEST_CASE("jit_load_attach_detach_unload_random_v4_test", "[jit_mt_stress_test]")
|
||||
{
|
||||
// This test attempts to load the same JIT'ed ebpf program multiple times in different threads. This test
|
||||
|
@ -1210,3 +1516,20 @@ TEST_CASE("sockaddr_invoke_program_test", "[native_mt_stress_test]")
|
|||
_print_test_control_info(local_test_control_info);
|
||||
_mt_sockaddr_invoke_program_test(local_test_control_info);
|
||||
}
|
||||
|
||||
TEST_CASE("bindmonitor_tail_call_invoke_program_test", "[native_mt_stress_test]")
|
||||
{
|
||||
// Test layout:
|
||||
// 1. Load the "bindmonitor_mt_tailcall.sys" native ebpf program.
|
||||
// 2. Load MAX_TAIL_CALL_CNT tail call programs.
|
||||
// 3. Create the specified number of threads.
|
||||
// - Each thread will invoke the TCP 'bind'.
|
||||
// - This will invoke MAX_TAIL_CALL_CNT tail call programs for permit.
|
||||
|
||||
_km_test_init();
|
||||
LOG_INFO("\nStarting test *** bindmonitor_tailcall_invoke_program_test ***");
|
||||
test_control_info local_test_control_info = _global_test_control_info;
|
||||
|
||||
_print_test_control_info(local_test_control_info);
|
||||
_mt_bindmonitor_tail_call_invoke_program_test(local_test_control_info);
|
||||
}
|
|
@ -103,6 +103,24 @@ Sample command line invocations:
|
|||
- Extension restart enabled.
|
||||
- Delay of 250 ms between successive extension restarts.
|
||||
|
||||
## 1.6. bindmonitor_tail_call_invoke_program_test
|
||||
This test first loads a specific native eBPF program. It then loads all the MAX_TAIL_CALL_CNT tail call programs and updates the program array map. It then creates the specified number of threads where each thread attempts a TCP 'bind' to the same port continuously in a loop. The test setup guarantees that the `thread_index` passed in each `thread_context` is unique to that thread, so that each thread gets a unique port (base_port + thread_index).
|
||||
|
||||
This causes the invocation of the in-kernel eBPF tail call programs to be executed in sequence. The last tail call program returns a PERMIT verdict.
|
||||
|
||||
This test can be run with or without the extension restart option.
|
||||
|
||||
Sample command line invocations:
|
||||
|
||||
### 1.6.1. `ebpf_stress_test_km bindmonitor_tail_call_invoke_program_test`
|
||||
- Uses default values for all supported options.
|
||||
|
||||
### 1.6.2. `ebpf_stress_test_km -tt=32 -td=15 -vo=true -er=true -erd=250 bindmonitor_tail_call_invoke_program_test`
|
||||
- Creates 32 test threads.
|
||||
- Runs test for 15 minutes.
|
||||
- Verbose test trace output enabled.
|
||||
- Extension restart enabled.
|
||||
- Delay of 250 ms between successive extension restarts.
|
||||
|
||||
# 2.0. ebpf_stress_test_um.exe (test sources in .\um\)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче