diff --git a/test/libsinsp_e2e/CMakeLists.txt b/test/libsinsp_e2e/CMakeLists.txt index 5e67bbc702a..ac4b15c8804 100755 --- a/test/libsinsp_e2e/CMakeLists.txt +++ b/test/libsinsp_e2e/CMakeLists.txt @@ -36,10 +36,13 @@ add_executable(libsinsp_e2e_tests container/container_cgroup.cpp container/docker_utils.cpp event_capture.cpp + fs.cpp main.cpp - paths.cpp + paths.cpp + process.cpp subprocess.cpp sys_call_test.cpp + threadinfo.cpp thread_state.cpp ) diff --git a/test/libsinsp_e2e/event_capture.cpp b/test/libsinsp_e2e/event_capture.cpp index eb5468db963..6726d30cfd9 100644 --- a/test/libsinsp_e2e/event_capture.cpp +++ b/test/libsinsp_e2e/event_capture.cpp @@ -111,18 +111,23 @@ void event_capture::capture() int32_t next_result = SCAP_SUCCESS; while (!m_capture_stopped && result && !::testing::Test::HasFatalFailure()) { - if (SCAP_SUCCESS == (next_result = get_inspector()->next(&event))) + { + std::scoped_lock inspector_next_lock(m_inspector_mutex); + next_result = get_inspector()->next(&event); + } + if (SCAP_SUCCESS == next_result) { result = handle_event(event); + if (m_mode != SINSP_MODE_NODRIVER) + { + dumper->dump(event); + } } if (!signaled_start) { signaled_start = true; - { - std::unique_lock lock(m_object_state_mutex); - m_capture_started = true; - m_condition_started.notify_one(); - } + m_capture_started = true; + m_condition_started.notify_one(); } } @@ -131,7 +136,10 @@ void event_capture::capture() uint32_t n_timeouts = 0; while (result && !::testing::Test::HasFatalFailure()) { - next_result = get_inspector()->next(&event); + { + std::scoped_lock inspector_next_lock(m_inspector_mutex); + next_result = get_inspector()->next(&event); + } if (next_result == SCAP_TIMEOUT) { n_timeouts++; @@ -172,6 +180,11 @@ void event_capture::capture() m_before_close(get_inspector()); get_inspector()->stop_capture(); + if (m_mode != SINSP_MODE_NODRIVER) + { + dumper->close(); + } + m_capture_stopped = true; m_condition_stopped.notify_one(); } // End teardown synchronized section diff --git a/test/libsinsp_e2e/fs.cpp b/test/libsinsp_e2e/fs.cpp new file mode 100644 index 00000000000..d58d919200b --- /dev/null +++ b/test/libsinsp_e2e/fs.cpp @@ -0,0 +1,1672 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include "event_capture.h" +#include "subprocess.h" +#include "sys_call_test.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DATA "AAAAAAAAA" + +#define FILENAME "test_tmpfile" +#define DIRNAME "test_tmpdir" +#define UNEXISTENT_DIRNAME "/unexistent/pippo" + +///////////////////////////////////////////////////////////////////////////////////// +// creat/unlink +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_creat_ulink) +{ + int callnum = 0; + char bcwd[1024]; + + ASSERT_TRUE(getcwd(bcwd, 1024) != NULL); + std::string cwd(bcwd); + cwd += "/"; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + int fd = creat(FILENAME, 0644); + + if (fd < 0) + { + FAIL(); + } + + ASSERT_TRUE(write(fd, "ABCD", sizeof("ABCD")) >= 0); + close(fd); + unlink(FILENAME); + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + std::string name(e->get_name()); + + #if defined(__x86_64__) + if (type == PPME_SYSCALL_CREAT_E) + #else + if (name.find("open") != std::string::npos && e->get_direction() == SCAP_ED_IN) + #endif + { + callnum++; + } + #if defined(__x86_64__) + else if (type == PPME_SYSCALL_CREAT_X) + #else + else if (name.find("open") != std::string::npos && e->get_direction() == SCAP_ED_OUT) +#endif + { + if (callnum == 1) + { + std::string fname = e->get_param_value_str("name", false); + if (fname == FILENAME) + { + EXPECT_EQ("0644", e->get_param_value_str("mode")); + } + + EXPECT_LT(0, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_UNLINK_2_E || type == PPME_SYSCALL_UNLINKAT_2_E) + { + if (callnum == 2 || callnum == 4) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_UNLINK_2_X || type == PPME_SYSCALL_UNLINKAT_2_X) + { + if (callnum == 3) + { + if(type == PPME_SYSCALL_UNLINK_2_X) + { + EXPECT_EQ(FILENAME, e->get_param_value_str("path", false)); + EXPECT_EQ(cwd + FILENAME, e->get_param_value_str("path")); + } + else + { + EXPECT_EQ(FILENAME, e->get_param_value_str("name", false)); + } + EXPECT_LE(0, std::stoi(e->get_param_value_str("res", false))); + callnum++; + } + else if (callnum == 5) + { + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(6, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// mkdir/rmdir +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_mkdir_rmdir) +{ + int callnum = 0; + char bcwd[1024]; + + ASSERT_TRUE(getcwd(bcwd, 1024) != NULL); + std::string cwd(bcwd); + cwd += "/"; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + mkdir(UNEXISTENT_DIRNAME, 0); + + if (mkdir(DIRNAME, 0) != 0) + { + FAIL(); + } + + if (rmdir(DIRNAME) != 0) + { + FAIL(); + } + + if (rmdir(DIRNAME) == 0) + { + FAIL(); + } + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_MKDIR_2_E) + { + if (callnum == 0) + { + EXPECT_EQ("0", e->get_param_value_str("mode")); + callnum++; + } + else if (callnum == 2) + { + EXPECT_EQ("0", e->get_param_value_str("mode")); + callnum++; + } + } + if (type == PPME_SYSCALL_MKDIRAT_E) + { + if (callnum == 0) + { + callnum++; + } + else if (callnum == 2) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_MKDIR_2_X || type == PPME_SYSCALL_MKDIRAT_X) + { + if (callnum == 1) + { + EXPECT_NE("0", e->get_param_value_str("res")); + EXPECT_EQ(UNEXISTENT_DIRNAME, e->get_param_value_str("path")); + EXPECT_EQ(UNEXISTENT_DIRNAME, e->get_param_value_str("path", false)); + callnum++; + } + else if (callnum == 3) + { + EXPECT_EQ("0", e->get_param_value_str("res")); + EXPECT_EQ(cwd + DIRNAME, e->get_param_value_str("path")); + EXPECT_EQ(DIRNAME, e->get_param_value_str("path", false)); + callnum++; + } + } + else if (type == PPME_SYSCALL_RMDIR_2_E || type == PPME_SYSCALL_UNLINKAT_2_E) + { + if (callnum == 4 || callnum == 6) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_RMDIR_2_X || type == PPME_SYSCALL_UNLINKAT_2_X) + { + if (callnum == 5) + { + EXPECT_LE(0, std::stoi(e->get_param_value_str("res", false))); + if (type == PPME_SYSCALL_RMDIR_2_X) + { + EXPECT_EQ(DIRNAME, e->get_param_value_str("path", false)); + EXPECT_EQ(cwd + DIRNAME, e->get_param_value_str("path")); + } + else + { + EXPECT_EQ(DIRNAME, e->get_param_value_str("name", false)); + } + callnum++; + } + else if (callnum == 7) + { + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + if (type == PPME_SYSCALL_RMDIR_2_X) + { + EXPECT_EQ(DIRNAME, e->get_param_value_str("path", false)); + EXPECT_EQ(cwd + DIRNAME, e->get_param_value_str("path")); + } + else + { + EXPECT_EQ(DIRNAME, e->get_param_value_str("name", false)); + } + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(8, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// openat +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_openat) +{ + int callnum = 0; + char bcwd[1024]; + int dirfd; + int fd1; + int fd2; + + ASSERT_TRUE(getcwd(bcwd, 1024) != NULL); + std::string cwd(bcwd); + cwd += "/"; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + dirfd = open(".", O_DIRECTORY); + if (dirfd <= 0) + { + FAIL(); + } + + // + // Generate a pagefault to make sure openat_enter doesn't + // get dropped because FILENAME is not available in memory + // + std::string s = FILENAME; + fd1 = openat(dirfd, FILENAME, O_CREAT | O_WRONLY, S_IRWXU | S_IRWXG | S_IRWXO); + if (fd1 <= 0) + { + FAIL(); + } + + ASSERT_TRUE(write(fd1, DATA, sizeof(DATA)) >= 0); + + close(fd1); + close(dirfd); + + unlink(FILENAME); + + fd2 = openat(AT_FDCWD, FILENAME, O_CREAT | O_WRONLY, S_IRWXU | S_IRWXG | S_IRWXO); + if (fd2 <= 0) + { + FAIL(); + } + + close(fd2); + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + std::string filepath = cwd + FILENAME; + + if (type == PPME_SYSCALL_OPENAT_2_X && + param.m_evt->get_param_value_str("name") == filepath && + (std::string("") + filepath) == e->get_param_value_str("fd")) + { + if (callnum == 0) + { + EXPECT_EQ(dirfd, std::stoll(e->get_param_value_str("dirfd", false))); + EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false))); + EXPECT_EQ(std::string("") + bcwd, e->get_param_value_str("dirfd")); + callnum++; + } + else if (callnum == 1) + { + EXPECT_EQ(-100, std::stoll(e->get_param_value_str("dirfd", false))); + EXPECT_EQ(fd2, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(2, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// pread/pwrite +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_pread) +{ + int callnum = 0; + char buf[32]; + int fd; + int fd1; + bool pwrite64_succeeded; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + fd = creat(FILENAME, S_IRWXU); + if (fd < 0) + { + FAIL(); + } + + ASSERT_TRUE(write(fd, "QWERTYUI", sizeof("QWERTYUI") - 1) >= 0); + ASSERT_TRUE(pwrite(fd, "ABCD", sizeof("ABCD") - 1, 4) >= 0); + ssize_t bytes_sent = pwrite64(fd, "ABCD", sizeof("ABCD") - 1, 987654321987654); + // + // On NFS, pwrite64 succeeds, so the test must evaluate the return + // code in the proper way + // + pwrite64_succeeded = bytes_sent > 0; + + ASSERT_TRUE(pread64(fd, buf, 32, 1234567891234) < 0); + close(fd); + + fd1 = open(FILENAME, O_RDONLY); + if (fd1 < 0) + { + FAIL(); + } + + ASSERT_TRUE(pread(fd1, buf, 4, 4) >= 0); + + close(fd1); + + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_WRITE_E) + { + if (std::stoll(e->get_param_value_str("fd", false)) == fd) + { + EXPECT_EQ((int)sizeof("QWERTYUI") - 1, + std::stoll(e->get_param_value_str("size", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_WRITE_X) + { + if (callnum == 1) + { + EXPECT_EQ((int)sizeof("QWERTYUI") - 1, + std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ("QWERTYUI", e->get_param_value_str("data")); + callnum++; + } + } + if (type == PPME_SYSCALL_PWRITE_E) + { + if (std::stoll(e->get_param_value_str("fd", false)) == fd) + { + if (callnum == 2) + { + EXPECT_EQ((int)sizeof("ABCD") - 1, + std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ("4", e->get_param_value_str("pos")); + callnum++; + } + else + { + EXPECT_EQ((int)sizeof("ABCD") - 1, + std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ("987654321987654", e->get_param_value_str("pos")); + callnum++; + } + } + } + else if (type == PPME_SYSCALL_PWRITE_X) + { + if (callnum == 3) + { + EXPECT_EQ((int)sizeof("ABCD") - 1, + std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ("ABCD", e->get_param_value_str("data")); + callnum++; + } + else + { + if (pwrite64_succeeded) + { + EXPECT_EQ((int)sizeof("ABCD") - 1, + std::stoi(e->get_param_value_str("res", false))); + } + else + { + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + } + EXPECT_EQ("ABCD", e->get_param_value_str("data")); + callnum++; + } + } + if (type == PPME_SYSCALL_PREAD_E) + { + if (callnum == 6) + { + EXPECT_EQ("32", e->get_param_value_str("size")); + EXPECT_EQ("1234567891234", e->get_param_value_str("pos")); + callnum++; + } + else if (callnum == 8) + { + EXPECT_EQ("4", e->get_param_value_str("size")); + EXPECT_EQ("4", e->get_param_value_str("pos")); + callnum++; + } + else + { + FAIL(); + } + } + else if (type == PPME_SYSCALL_PREAD_X) + { + if (callnum == 7) + { + EXPECT_NE("0", e->get_param_value_str("res", false)); + callnum++; + } + else if (callnum == 9) + { + EXPECT_EQ((int)sizeof("ABCD") - 1, + std::stoi(e->get_param_value_str("res", false))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(10, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// writev/readv +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_readv) +{ + int callnum = 0; + int fd; + int fd1; + int bytes_sent; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + int wv_count; + char msg1[10] = "aaaaa"; + char msg2[10] = "bbbbb"; + char msg3[10] = "ccccc"; + struct iovec wv[3]; + int rres; + + fd = open(FILENAME, O_CREAT | O_WRONLY, S_IRWXU); + + wv[0].iov_base = msg1; + wv[1].iov_base = msg2; + wv[2].iov_base = msg3; + wv[0].iov_len = strlen(msg1); + wv[1].iov_len = strlen(msg2); + wv[2].iov_len = strlen(msg3); + wv_count = 3; + + bytes_sent = writev(fd, wv, wv_count); + if (bytes_sent <= 0) + { + FAIL(); + } + + close(fd); + + fd1 = open(FILENAME, O_CREAT | O_RDONLY, S_IRWXU); + + wv[0].iov_len = sizeof(msg1); + wv[1].iov_len = sizeof(msg2); + wv[2].iov_len = sizeof(msg3); + + rres = readv(fd1, wv, wv_count); + if (rres <= 0) + { + FAIL(); + } + + close(fd1); + + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_WRITEV_E) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + EXPECT_EQ(15, std::stoll(e->get_param_value_str("size"))); + callnum++; + } + else if (type == PPME_SYSCALL_WRITEV_X) + { + if (callnum == 1) + { + EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data")); + callnum++; + } + } + else if (type == PPME_SYSCALL_READV_E) + { + EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + else if (type == PPME_SYSCALL_READV_X) + { + if (callnum == 3) + { + EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ("aaaaabbbbbccccc", (e->get_param_value_str("data")).substr(0, 15)); + EXPECT_EQ(15, std::stoll(e->get_param_value_str("size"))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(4, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// pwritev/preadv +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_preadv) +{ + int callnum = 0; + int fd; + int fd1; + int bytes_sent; + bool pwritev64_succeeded; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + int wv_count; + char msg1[10] = "aaaaa"; + char msg2[10] = "bbbbb"; + char msg3[10] = "ccccc"; + struct iovec wv[3]; + int rres; + fd = open(FILENAME, O_CREAT | O_WRONLY, S_IRWXU); + + ASSERT_TRUE(write(fd, "123456789012345678901234567890", sizeof("QWERTYUI") - 1) >= 0); + + wv[0].iov_base = msg1; + wv[1].iov_base = msg2; + wv[2].iov_base = msg3; + wv[0].iov_len = strlen(msg1); + wv[1].iov_len = strlen(msg2); + wv[2].iov_len = strlen(msg3); + wv_count = 3; + + bytes_sent = pwritev64(fd, wv, wv_count, 132456789012345LL); + // + // On NFS, pwritev64 succeeds, so the test must evaluate the return + // code in the proper way + // + pwritev64_succeeded = bytes_sent > 0; + + bytes_sent = pwritev(fd, wv, wv_count, 10); + if (bytes_sent <= 0) + { + FAIL(); + } + + close(fd); + + fd1 = open(FILENAME, O_CREAT | O_RDONLY, S_IRWXU); + + wv[0].iov_len = sizeof(msg1); + wv[1].iov_len = sizeof(msg2); + wv[2].iov_len = sizeof(msg3); + + rres = preadv64(fd1, wv, wv_count, 987654321098); + + rres = preadv(fd1, wv, wv_count, 10); + if (rres <= 0) + { + FAIL(); + } + + close(fd1); + + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_PWRITEV_E) + { + if (callnum == 0) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + EXPECT_EQ(15, std::stoll(e->get_param_value_str("size"))); + EXPECT_EQ(132456789012345LL, std::stoll(e->get_param_value_str("pos"))); + callnum++; + } + else + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + EXPECT_EQ(10, std::stoll(e->get_param_value_str("pos"))); + EXPECT_EQ(15, std::stoll(e->get_param_value_str("size"))); + callnum++; + } + } + else if (type == PPME_SYSCALL_PWRITEV_X) + { + if (callnum == 1) + { + if (pwritev64_succeeded) + { + EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false))); + } + else + { + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + } + + EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data")); + callnum++; + } + else + { + EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ("aaaaabbbbbccccc", e->get_param_value_str("data")); + callnum++; + } + } + else if (type == PPME_SYSCALL_PREADV_E) + { + if (callnum == 4) + { + EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false))); + EXPECT_EQ(987654321098, std::stoll(e->get_param_value_str("pos"))); + callnum++; + } + else + { + EXPECT_EQ(fd1, std::stoll(e->get_param_value_str("fd", false))); + EXPECT_EQ(10, std::stoll(e->get_param_value_str("pos"))); + callnum++; + } + } + else if (type == PPME_SYSCALL_PREADV_X) + { + if (callnum == 3) + { + EXPECT_EQ(15, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ("aaaaabbbbb", e->get_param_value_str("data")); + EXPECT_EQ(30, std::stoll(e->get_param_value_str("size"))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + // EXPECT_EQ(4, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// dup +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_dup) +{ + int callnum = 0; + int fd; + int fd1; + int fd2; + int fd3; + int fd4; + int fd5; + int fd6; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) + { + uint16_t type = evt->get_type); + return m_tid_filter(evt) && + (type == PPME_SYSCALL_DUP_1_E || type == PPME_SYSCALL_DUP2_E || + type == PPME_SYSCALL_DUP3_E || type == PPME_SYSCALL_DUP_E || + type == PPME_SYSCALL_DUP_1_X || type == PPME_SYSCALL_DUP2_X || + type == PPME_SYSCALL_DUP3_X || type == PPME_SYSCALL_DUP_X); + }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + fd = open(FILENAME, O_CREAT | O_WRONLY, 0); + fd1 = dup(fd); + fd2 = dup2(fd, 333); + EXPECT_EQ(333, fd2); + fd3 = dup2(fd, fd1); + EXPECT_EQ(fd3, fd1); + fd4 = dup3(fd3, 444, O_CLOEXEC); + EXPECT_EQ(444, fd4); + fd5 = dup2(-1, 33); + EXPECT_EQ(-1, fd5); + fd6 = dup2(fd, fd); + EXPECT_EQ(fd6, fd); + + close(fd); + close(fd1); + close(fd2); + close(fd3); + close(fd4); + + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + if (type == PPME_SYSCALL_DUP_1_E || type == PPME_SYSCALL_DUP2_E || + type == PPME_SYSCALL_DUP3_E || type == PPME_SYSCALL_DUP_E) + { + if (callnum == 0) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + else if (callnum == 2) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + else if (callnum == 4) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + else if (callnum == 6) + { + EXPECT_EQ(fd3, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + else if (callnum == 8) + { + EXPECT_EQ("-1", e->get_param_value_str("fd", false)); + callnum++; + } + else if (callnum == 10) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_DUP_1_X || type == PPME_SYSCALL_DUP2_X || + type == PPME_SYSCALL_DUP3_X || type == PPME_SYSCALL_DUP_X) + { + ASSERT_NE( + (sinsp_threadinfo*)&*param.m_inspector->get_thread_ref(e->get_tid(), false, true), + nullptr); + if (callnum == 1) + { + EXPECT_EQ(fd1, std::stoi(e->get_param_value_str("res", false))); + EXPECT_NE((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd1)); + callnum++; + } + else if (callnum == 3) + { + EXPECT_EQ(fd2, std::stoi(e->get_param_value_str("res", false))); + EXPECT_NE((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd2)); + callnum++; + } + else if (callnum == 5) + { + EXPECT_EQ(fd3, std::stoi(e->get_param_value_str("res", false))); + EXPECT_NE((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd3)); + callnum++; + } + else if (callnum == 7) + { + EXPECT_EQ(fd4, std::stoi(e->get_param_value_str("res", false))); + EXPECT_NE((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd4)); + callnum++; + } + else if (callnum == 9) + { + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd5)); + callnum++; + } + else if (callnum == 11) + { + EXPECT_EQ(fd6, std::stoi(e->get_param_value_str("res", false))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + +#if defined(__x86_64__) + EXPECT_EQ(12, callnum); +#else + // On arm the last dup is skipped: a fcntl is called instead. + EXPECT_EQ(10, callnum); +#endif +} + +///////////////////////////////////////////////////////////////////////////////////// +// fcntl +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_fcntl) +{ + int callnum = 0; + int fd; + int fd1; + int fd2; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + fd = open(FILENAME, O_CREAT | O_WRONLY, 0); + fd1 = fcntl(fd, F_DUPFD, 0); + fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 0); + printf("fd: %d %d %d, errno: %d\n", fd, fd1, fd2, errno); + + close(fd); + close(fd1); + close(fd2); + + unlink(FILENAME); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_FCNTL_E) + { + if (callnum == 0) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + else if (callnum == 2) + { + EXPECT_EQ(fd, std::stoll(e->get_param_value_str("fd", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_FCNTL_X) + { + ASSERT_NE( + (sinsp_threadinfo*)&*param.m_inspector->get_thread_ref(e->get_tid(), false, true), + nullptr); + if (callnum == 1) + { + EXPECT_EQ(fd1, std::stoi(e->get_param_value_str("res", false))); + EXPECT_NE((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd1)); + callnum++; + } + else if (callnum == 3) + { + EXPECT_EQ(fd2, std::stoi(e->get_param_value_str("res", false))); + EXPECT_NE((sinsp_threadinfo*)NULL, + (sinsp_threadinfo*)&*param.m_inspector + ->get_thread_ref(e->get_tid(), false, true) + ->get_fd(fd1)); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(4, callnum); +} + +///////////////////////////////////////////////////////////////////////////////////// +// sendfile +///////////////////////////////////////////////////////////////////////////////////// +TEST_F(sys_call_test, fs_sendfile) +{ + int callnum = 0; + int read_fd; + int write_fd; + int size; + off_t offset = 0; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + struct stat stat_buf; + + read_fd = open("/etc/passwd", O_RDONLY); + EXPECT_LE(0, read_fd); + + fstat(read_fd, &stat_buf); + + write_fd = open("out.txt", O_WRONLY | O_CREAT, stat_buf.st_mode); + EXPECT_LE(0, write_fd); + + size = stat_buf.st_size; + int res = sendfile(write_fd, read_fd, &offset, size); + EXPECT_LE(0, res); + + close(read_fd); + close(write_fd); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_SENDFILE_E) + { + EXPECT_EQ(write_fd, std::stoll(e->get_param_value_str("out_fd", false))); + EXPECT_EQ(read_fd, std::stoll(e->get_param_value_str("in_fd", false))); + EXPECT_EQ(size, std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + else if (type == PPME_SYSCALL_SENDFILE_X) + { + EXPECT_LE(0, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ(offset, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(2, callnum); +} + +TEST_F(sys_call_test, fs_sendfile_nulloff) +{ + int callnum = 0; + int read_fd; + int write_fd; + int size; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + struct stat stat_buf; + + read_fd = open("/etc/passwd", O_RDONLY); + EXPECT_LE(0, read_fd); + + fstat(read_fd, &stat_buf); + + write_fd = open("out.txt", O_WRONLY | O_CREAT, stat_buf.st_mode); + EXPECT_LE(0, write_fd); + + size = stat_buf.st_size; + int res = sendfile(write_fd, read_fd, NULL, size); + EXPECT_LE(0, res); + + close(read_fd); + close(write_fd); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_SENDFILE_E) + { + EXPECT_EQ(write_fd, std::stoll(e->get_param_value_str("out_fd", false))); + EXPECT_EQ(read_fd, std::stoll(e->get_param_value_str("in_fd", false))); + EXPECT_EQ(size, std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + else if (type == PPME_SYSCALL_SENDFILE_X) + { + EXPECT_LE(0, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(2, callnum); +} + +TEST_F(sys_call_test, fs_sendfile_failed) +{ + int callnum = 0; + // int size; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + int res = sendfile(-1, -2, NULL, 444); + EXPECT_GT(0, res); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_SENDFILE_E) + { + EXPECT_NO_THROW({ + EXPECT_EQ("-1", e->get_param_value_str("out_fd", false)); + EXPECT_EQ("-2", e->get_param_value_str("in_fd", false)); + EXPECT_EQ(444, std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + }); + + callnum++; + } + else if (type == PPME_SYSCALL_SENDFILE_X) + { + EXPECT_NO_THROW({ + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + }); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(2, callnum); +} + +TEST_F(sys_call_test, fs_sendfile_invalidoff) +{ + int callnum = 0; + int read_fd; + int write_fd; + int size; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + struct stat stat_buf; + + read_fd = open("/etc/passwd", O_RDONLY); + EXPECT_LE(0, read_fd); + + fstat(read_fd, &stat_buf); + + write_fd = open("out.txt", O_WRONLY | O_CREAT, stat_buf.st_mode); + EXPECT_LE(0, write_fd); + + size = stat_buf.st_size; + int res = sendfile(write_fd, read_fd, (off_t*)3333, size); + EXPECT_GT(0, res); + + close(read_fd); + close(write_fd); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_SENDFILE_E) + { + EXPECT_EQ(write_fd, std::stoll(e->get_param_value_str("out_fd", false))); + EXPECT_EQ(read_fd, std::stoll(e->get_param_value_str("in_fd", false))); + EXPECT_EQ(size, std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + else if (type == PPME_SYSCALL_SENDFILE_X) + { + EXPECT_GT(0, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(2, callnum); +} + +#ifdef __i386__ +TEST_F(sys_call_test, fs_sendfile64) +{ + int callnum = 0; + int read_fd; + int write_fd; + int size; + loff_t offset = 0; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector) + { + struct stat stat_buf; + + read_fd = open("/etc/passwd", O_RDONLY); + EXPECT_LE(0, read_fd); + + fstat(read_fd, &stat_buf); + + write_fd = open("out.txt", O_WRONLY | O_CREAT, stat_buf.st_mode); + EXPECT_LE(0, write_fd); + + size = stat_buf.st_size; + int res = syscall(SYS_sendfile64, write_fd, read_fd, &offset, size); + EXPECT_LE(0, res); + + close(read_fd); + close(write_fd); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_SENDFILE_E) + { + EXPECT_EQ(write_fd, std::stoll(e->get_param_value_str("out_fd", false))); + EXPECT_EQ(read_fd, std::stoll(e->get_param_value_str("in_fd", false))); + EXPECT_EQ(size, std::stoll(e->get_param_value_str("size", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + else if (type == PPME_SYSCALL_SENDFILE_X) + { + EXPECT_LE(0, std::stoi(e->get_param_value_str("res", false))); + EXPECT_EQ(offset, std::stoll(e->get_param_value_str("offset", false))); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(2, callnum); +} +#endif + +TEST_F(sys_call_test, large_read_write) +{ + const int buf_size = PPM_MAX_ARG_SIZE * 10; + + std::vector buf(buf_size); + int callnum = 0; + int fd1, fd2; + + srandom(42); + + before_open_t setup = [&](sinsp* inspector) + { + inspector->set_snaplen(SNAPLEN_MAX); + }; + + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + + fd1 = creat(FILENAME, S_IRWXU); + if (fd1 < 0) + { + FAIL(); + } + + int res = write(fd1, buf.data(), buf_size); + EXPECT_EQ(res, buf_size); + + close(fd1); + + fd2 = open(FILENAME, O_RDONLY); + if (fd2 < 0) + { + FAIL(); + } + + res = read(fd2, buf.data(), buf_size); + EXPECT_EQ(res, buf_size); + + close(fd2); + + unlink(FILENAME); + }; + + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_WRITE_E) + { + if (std::stoll(e->get_param_value_str("fd", false)) == fd1) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_WRITE_X) + { + if (callnum == 1) + { + const sinsp_evt_param* p = e->get_param_by_name("data"); + + EXPECT_EQ(p->m_len, SNAPLEN_MAX); + EXPECT_EQ(0, memcmp(buf.data(), p->m_val, SNAPLEN_MAX)); + + callnum++; + } + } + if (type == PPME_SYSCALL_READ_E) + { + if (callnum == 2) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_READ_X) + { + if (callnum == 3) + { + const sinsp_evt_param* p = e->get_param_by_name("data"); + + EXPECT_EQ(p->m_len, SNAPLEN_MAX); + EXPECT_EQ(0, memcmp(buf.data(), p->m_val, SNAPLEN_MAX)); + + callnum++; + } + } + }; + + before_close_t cleanup = [&](sinsp* inspector) + { + inspector->set_snaplen(DEFAULT_SNAPLEN); + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); }); + + EXPECT_EQ(4, callnum); +} + +TEST_F(sys_call_test, large_readv_writev) +{ + const int buf_size = PPM_MAX_ARG_SIZE * 10; + const int chunks = 10; + + char buf[buf_size]; + int callnum = 0; + int fd; + + srandom(42); + + for (int j = 0; j < buf_size; ++j) + { + buf[j] = random(); + } + + before_open_t setup = [&](sinsp* inspector) + { + inspector->set_snaplen(SNAPLEN_MAX); + }; + + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + fd = creat(FILENAME, S_IRWXU); + if (fd < 0) + { + FAIL(); + } + + struct iovec iovs[chunks]; + int chunk_size = buf_size / chunks; + + int off = 0; + for (int j = 0; j < chunks; ++j) + { + iovs[j].iov_base = buf + off; + iovs[j].iov_len = chunk_size; + + off += chunk_size; + } + + int res = writev(fd, iovs, chunks); + EXPECT_EQ(res, (int)sizeof(buf)); + + close(fd); + + int fd = open(FILENAME, O_RDONLY); + if (fd < 0) + { + FAIL(); + } + + res = readv(fd, iovs, chunks); + EXPECT_EQ(res, (int)sizeof(buf)); + + close(fd); + + unlink(FILENAME); + }; + + captured_event_callback_t callback = [&](const callback_param& param) + { + const int max_kmod_buf = getpagesize() - sizeof(struct iovec) * chunks - 1; + (void)max_kmod_buf; + + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_WRITEV_E) + { + if (std::stoll(e->get_param_value_str("fd", false)) == fd) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_WRITEV_X) + { + if (callnum == 1) + { + const sinsp_evt_param* p = e->get_param_by_name("data"); + if(event_capture::m_engine_string == KMOD_ENGINE) + { + // + // The driver doesn't have the correct behavior for accumulating + // readv/writev, and it uses a single page as a temporary storage area + // + EXPECT_EQ(p->m_len, max_kmod_buf); + EXPECT_EQ(0, memcmp(buf, p->m_val, max_kmod_buf)); + } + else + { + EXPECT_EQ(p->m_len, SNAPLEN_MAX); + EXPECT_EQ(0, memcmp(buf, p->m_val, SNAPLEN_MAX)); + } + + callnum++; + } + } + if (type == PPME_SYSCALL_READV_E) + { + if (callnum == 2) + { + callnum++; + } + } + else if (type == PPME_SYSCALL_READV_X) + { + if (callnum == 3) + { + const sinsp_evt_param* p = e->get_param_by_name("data"); + if(event_capture::m_engine_string == KMOD_ENGINE) + { + EXPECT_EQ(p->m_len, max_kmod_buf); + EXPECT_EQ(0, memcmp(buf, p->m_val, max_kmod_buf)); + } + else + { + EXPECT_EQ(p->m_len, SNAPLEN_MAX); + EXPECT_EQ(0, memcmp(buf, p->m_val, SNAPLEN_MAX)); + } + + callnum++; + } + } + }; + + before_close_t cleanup = [&](sinsp* inspector) + { + inspector->set_snaplen(DEFAULT_SNAPLEN); + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter, setup, cleanup); }); + + EXPECT_EQ(4, callnum); +} + +TEST_F(sys_call_test, large_open) +{ + const int buf_size = PPM_MAX_ARG_SIZE * 10; + + int callnum = 0; + + srandom(42); + + std::string buf; + while (buf.length() < buf_size) + { + buf.append(std::to_string(random())); + } + + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + run_callback_t test = [&](concurrent_object_handle inspector) + { +#ifdef SYS_open + int fd = syscall(SYS_open, buf.c_str(), O_RDONLY); +#else + int fd = syscall(SYS_openat, AT_FDCWD, buf.c_str(), O_RDONLY); +#endif + EXPECT_EQ(fd, -1); + }; + + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + std::string name(e->get_name()); + + if (name.find("open") != std::string::npos && e->get_direction() == SCAP_ED_IN) + { + callnum++; + } + else if (name.find("open") != std::string::npos && e->get_direction() == SCAP_ED_OUT) + { + const sinsp_evt_param* p = e->get_param_by_name("name"); + + if(event_capture::m_engine_string == KMOD_ENGINE) + { + EXPECT_EQ(p->m_len, PPM_MAX_ARG_SIZE); + EXPECT_EQ(buf.substr(0, PPM_MAX_ARG_SIZE - 1), std::string(p->m_val)); + } + else if(event_capture::m_engine_string == BPF_ENGINE) + { + EXPECT_EQ(p->m_len, SNAPLEN_MAX); + EXPECT_EQ(buf.substr(0, SNAPLEN_MAX - 1), std::string(p->m_val)); + } + else if(event_capture::m_engine_string == MODERN_BPF_ENGINE) + { + EXPECT_EQ(p->m_len, PATH_MAX); + EXPECT_EQ(buf.substr(0, PATH_MAX - 1), std::string(p->m_val)); + } + + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + EXPECT_EQ(2, callnum); +} diff --git a/test/libsinsp_e2e/process.cpp b/test/libsinsp_e2e/process.cpp new file mode 100644 index 00000000000..f960fbb397d --- /dev/null +++ b/test/libsinsp_e2e/process.cpp @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sys_call_test.h" +#include "subprocess.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +TEST_F(sys_call_test, process_signalfd_kill) +{ + int callnum = 0; + + int ptid; // parent tid + int ctid; // child tid + int gptid; // grandparent tid + int xstatus = 33; // child exit value + int ssfd; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) + { return evt->get_tid() == ptid || evt->get_tid() == ctid; }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + int status; + int sfd; + ctid = fork(); + + if (ctid >= 0) // fork succeeded + { + if (ctid == 0) + { + // + // CHILD PROCESS + // + sigset_t mask; + + /* We will handle SIGTERM and SIGINT. */ + sigemptyset(&mask); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGINT); + + /* Block the signals that we handle using signalfd(), so they don't + * cause signal handlers or default signal actions to execute. */ + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + { + FAIL(); + } + + /* Create a file descriptor from which we will read the signals. */ + sfd = signalfd(-1, &mask, 0); + if (sfd < 0) + { + FAIL(); + } + + while (true) + { + /** The buffer for read(), this structure contains information + * about the signal we've read. */ + struct signalfd_siginfo si; + + ssize_t res; + + res = read(sfd, &si, sizeof(si)); + + if (res < 0) + { + FAIL(); + } + if (res != sizeof(si)) + { + FAIL(); + } + + if (si.ssi_signo == SIGTERM) + { + continue; + } + else if (si.ssi_signo == SIGINT) + { + break; + } + else + { + FAIL(); + } + } + + /* Close the file descriptor if we no longer need it. */ + close(sfd); + + sleep(1); + + // + // Remember to use _exit or the test system will get fucked!! + // + _exit(xstatus); + } + else + { + // + // PARENT PROCESS + // + ptid = getpid(); + gptid = getppid(); + + // + // Give the client some time install its handlers + // + usleep(200000); + + kill(ctid, SIGTERM); + kill(ctid, SIGINT); + + // + // Wait for child to exit, and store its status + // + ASSERT_EQ(waitpid(ctid, &status, 0), ctid); + } + } + else + { + FAIL(); + } + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_SIGNALFD_E) + { + EXPECT_EQ(-1, std::stoi(e->get_param_value_str("fd", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("mask"))); + EXPECT_EQ(0, std::stol(e->get_param_value_str("flags"))); + callnum++; + } + else if (type == PPME_SYSCALL_SIGNALFD4_E) + { + EXPECT_EQ(-1, stoi(e->get_param_value_str("fd", false))); + EXPECT_EQ(0, std::stoll(e->get_param_value_str("mask"))); + callnum++; + } + else if (type == PPME_SYSCALL_SIGNALFD_X || type == PPME_SYSCALL_SIGNALFD4_X) + { + ssfd = std::stoi(e->get_param_value_str("res", false)); + callnum++; + } + else if (type == PPME_SYSCALL_READ_E) + { + if (callnum == 2) + { + EXPECT_EQ("", e->get_param_value_str("fd")); + EXPECT_EQ(ssfd, std::stoi(e->get_param_value_str("fd", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_KILL_E) + { + if (callnum == 3) + { + EXPECT_EQ("libsinsp_e2e_te", e->get_param_value_str("pid")); + EXPECT_EQ(ctid, std::stoi(e->get_param_value_str("pid", false))); + EXPECT_EQ("SIGTERM", e->get_param_value_str("sig")); + EXPECT_EQ(SIGTERM, std::stoi(e->get_param_value_str("sig", false))); + callnum++; + } + else if (callnum == 5) + { + EXPECT_EQ("libsinsp_e2e_te", e->get_param_value_str("pid")); + EXPECT_EQ(ctid, std::stoi(e->get_param_value_str("pid", false))); + EXPECT_EQ("SIGINT", e->get_param_value_str("sig")); + EXPECT_EQ(SIGINT, std::stoi(e->get_param_value_str("sig", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_KILL_X) + { + EXPECT_EQ(0, std::stoi(e->get_param_value_str("res", false))); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(7, callnum); +} + +// This test is disabled until the new syscall for sleep is implemented. +TEST_F(sys_call_test, DISABLED_process_usleep) +{ + int callnum = 0; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [](concurrent_object_handle inspector_handle) + { + + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 123456; + nanosleep(&req,nullptr); + req.tv_sec = 5; + req.tv_nsec = 0; + nanosleep(&req,nullptr); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_NANOSLEEP_E) + { + if (callnum == 0) + { + if (std::stoll(e->get_param_value_str("interval", false)) == 123456000) + { + callnum++; + } + } + else if (callnum == 2) + { + EXPECT_EQ(5000000000, std::stoll(e->get_param_value_str("interval", false))); + callnum++; + } + } + else if (type == PPME_SYSCALL_NANOSLEEP_X) + { + EXPECT_EQ(0, stoi(e->get_param_value_str("res", false))); + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(4, callnum); +} + +#define EVENT_SIZE (sizeof(struct inotify_event)) +#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16)) + +TEST_F(sys_call_test, process_inotify) +{ + int callnum = 0; + int fd; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + int length; + int wd; + char buffer[EVENT_BUF_LEN]; + + // + // creating the INOTIFY instance + // + fd = inotify_init(); + + /*checking for error*/ + if (fd < 0) + { + FAIL(); + } + + // + // The IN_MODIFY flag causes a notification when a file is written, which should + // heppen immediately in captures + // + wd = inotify_add_watch(fd, LIBSINSP_TEST_CAPTURES_PATH, IN_MODIFY | IN_CREATE | IN_OPEN); + + // + // read to determine the event changes + // + length = read(fd, buffer, EVENT_BUF_LEN); + if (length < 0) + { + FAIL(); + } + + // + // removing the watch + // + inotify_rm_watch(fd, wd); + + // + // closing the INOTIFY instance + // + close(fd); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + std::string name(e->get_name()); + + if (type == PPME_SYSCALL_INOTIFY_INIT_E) + { + EXPECT_EQ(0, std::stoi(e->get_param_value_str("flags"))); + callnum++; + } + else if (type == PPME_SYSCALL_INOTIFY_INIT1_E) + { + callnum++; + } + else if (type == PPME_SYSCALL_INOTIFY_INIT_X || type == PPME_SYSCALL_INOTIFY_INIT1_X) + { + EXPECT_EQ(fd, std::stoi(e->get_param_value_str("res", false))); + callnum++; + } + else if (name.find("read") != std::string::npos && e->get_direction() == SCAP_ED_IN) + { + if (callnum == 2) + { + EXPECT_EQ("", e->get_param_value_str("fd")); + EXPECT_EQ(fd, std::stoi(e->get_param_value_str("fd", false))); + callnum++; + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(3, callnum); +} + +TEST(procinfo, process_not_existent) +{ + sinsp inspector; + inspector.open_nodriver(true); + + // + // The first lookup should fail + // + EXPECT_EQ(NULL, inspector.get_thread_ref(0xffff, false, true).get()); + + // + // Even the second, to confirm that nothing was added to the table + // + EXPECT_EQ(NULL, inspector.get_thread_ref(0xffff, false, true).get()); + + // + // Now a new entry should be added to the process list... + // + sinsp_threadinfo* tinfo = inspector.get_thread_ref(0xffff, true, true).get(); + EXPECT_NE((sinsp_threadinfo*)NULL, tinfo); + if (tinfo) + { + EXPECT_EQ("", tinfo->m_comm); + } + + // + // ...and confirm + // + tinfo = inspector.get_thread_ref(0xffff, false, true).get(); + EXPECT_NE((sinsp_threadinfo*)NULL, tinfo); + if (tinfo) + { + EXPECT_EQ("", tinfo->m_comm); + } + + inspector.close(); +} + +TEST_F(sys_call_test, process_rlimit) +{ + int callnum = 0; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [](concurrent_object_handle inspector_handle) + { + struct rlimit rl; + sleep(1); + + // Called directly because libc likes prlimit() + syscall(SYS_getrlimit, RLIMIT_NOFILE, (struct rlimit*)33); + syscall(SYS_getrlimit, RLIMIT_NOFILE, &rl); + rl.rlim_cur = 500; + rl.rlim_max = 1000; + syscall(SYS_setrlimit, RLIMIT_NOFILE, &rl); + syscall(SYS_getrlimit, RLIMIT_NOFILE, &rl); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_GETRLIMIT_E) + { + EXPECT_EQ((int64_t)PPM_RLIMIT_NOFILE, + std::stoll(e->get_param_value_str("resource", false))); + callnum++; + } + if (type == PPME_SYSCALL_GETRLIMIT_X) + { + if (callnum == 1) + { + EXPECT_GT((int64_t)0, std::stoll(e->get_param_value_str("res", false))); + } + else + { + EXPECT_EQ((int64_t)0, std::stoll(e->get_param_value_str("res", false))); + + if (callnum == 7) + { + EXPECT_EQ((int64_t)500, + std::stoll(e->get_param_value_str("cur", false))); + EXPECT_EQ((int64_t)1000, + std::stoll(e->get_param_value_str("max", false))); + } + } + + callnum++; + } + if (type == PPME_SYSCALL_SETRLIMIT_E) + { + EXPECT_EQ((int64_t)PPM_RLIMIT_NOFILE, + std::stoll(e->get_param_value_str("resource", false))); + callnum++; + } + if (type == PPME_SYSCALL_SETRLIMIT_X) + { + EXPECT_EQ((int64_t)0, std::stoll(e->get_param_value_str("res", false))); + + if (callnum == 5) + { + EXPECT_EQ((int64_t)500, + std::stoll(e->get_param_value_str("cur", false))); + EXPECT_EQ((int64_t)1000, + std::stoll(e->get_param_value_str("max", false))); + } + + callnum++; + } + if (type == PPME_SYSCALL_PRLIMIT_E) + { + EXPECT_EQ((int64_t)PPM_RLIMIT_NOFILE, + std::stoll(e->get_param_value_str("resource", false))); + callnum++; + } + if (type == PPME_SYSCALL_PRLIMIT_X) + { + int64_t res = std::stoll(e->get_param_value_str("res", false)); + int64_t newcur = std::stoll(e->get_param_value_str("newcur", false)); + int64_t newmax = std::stoll(e->get_param_value_str("newmax", false)); + int64_t oldcur = std::stoll(e->get_param_value_str("oldcur", false)); + int64_t oldmax = std::stoll(e->get_param_value_str("oldmax", false)); + switch (callnum) + { + case 1: + EXPECT_GT(0, res); + break; + case 3: + EXPECT_EQ(0, res); + EXPECT_EQ(-1, newcur); + EXPECT_EQ(-1, newmax); + break; + case 5: + EXPECT_EQ(0, res); + EXPECT_EQ(500, newcur); + EXPECT_EQ(1000, newmax); + EXPECT_EQ(-1, oldcur); + EXPECT_EQ(-1, oldmax); + break; + case 7: + EXPECT_EQ(0, res); + EXPECT_EQ(-1, newcur); + EXPECT_EQ(-1, newmax); + EXPECT_EQ(500, oldcur); + EXPECT_EQ(1000, oldmax); + break; + } + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(8, callnum); +} + +TEST_F(sys_call_test, process_prlimit) +{ + int callnum = 0; + struct rlimit tmprl; + struct rlimit orirl; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) + { + return m_tid_filter(evt); + }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + struct rlimit newrl; + struct rlimit oldrl; + + syscall(SYS_prlimit64, getpid(), RLIMIT_NOFILE, NULL, &orirl); + newrl.rlim_cur = 500; + newrl.rlim_max = 1000; + syscall(SYS_prlimit64, getpid(), RLIMIT_NOFILE, &newrl, &oldrl); + syscall(SYS_prlimit64, getpid(), RLIMIT_NOFILE, NULL, &oldrl); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_PRLIMIT_E) + { + EXPECT_EQ((int64_t)PPM_RLIMIT_NOFILE, + std::stoll(e->get_param_value_str("resource", false))); + EXPECT_EQ((int64_t)getpid(), + std::stoll(e->get_param_value_str("pid", false))); + callnum++; + } + else if(type == PPME_SYSCALL_PRLIMIT_X) + { + EXPECT_GE((int64_t)0, std::stoll(e->get_param_value_str("res", false))); + + if (callnum == 1) + { + EXPECT_EQ((int64_t)0, + std::stoll(e->get_param_value_str("newcur", false))); + EXPECT_EQ((int64_t)0, + std::stoll(e->get_param_value_str("newmax", false))); + EXPECT_EQ((int64_t)orirl.rlim_cur, + std::stoll(e->get_param_value_str("oldcur", false))); + EXPECT_EQ((int64_t)orirl.rlim_max, + std::stoll(e->get_param_value_str("oldmax", false))); + } + else if (callnum == 3) + { + EXPECT_EQ((int64_t)500, + std::stoll(e->get_param_value_str("newcur", false))); + EXPECT_EQ((int64_t)1000, + std::stoll(e->get_param_value_str("newmax", false))); + EXPECT_EQ((int64_t)orirl.rlim_cur, + std::stoll(e->get_param_value_str("oldcur", false))); + EXPECT_EQ((int64_t)orirl.rlim_max, + std::stoll(e->get_param_value_str("oldmax", false))); + } + else if (callnum == 5) + { + EXPECT_EQ((int64_t)0, + std::stoll(e->get_param_value_str("newcur", false))); + EXPECT_EQ((int64_t)0, + std::stoll(e->get_param_value_str("newmax", false))); + EXPECT_EQ((int64_t)500, + std::stoll(e->get_param_value_str("oldcur", false))); + EXPECT_EQ((int64_t)1000, + std::stoll(e->get_param_value_str("oldmax", false))); + } + + callnum++; + } + }; + + if (syscall(SYS_prlimit64, getpid(), RLIMIT_NOFILE, NULL, &tmprl) != 0) + { + return; + } + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); + + EXPECT_EQ(6, callnum); +} + +class loadthread +{ +public: + loadthread() + { + m_die = false; + m_tid = -1; + m_utime_delta = 0; + m_prevutime = 0; + } + + uint64_t read_utime() + { + struct rusage ru; + getrusage(RUSAGE_THREAD, &ru); + return ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec; + } + + void run() + { + uint64_t k = 0; + uint64_t t = 0; + m_tid = syscall(SYS_gettid); + + m_prevutime = read_utime(); + + while (true) + { + t += k; + t = t % 35689; + + if (m_read_cpu) + { + auto utime = read_utime(); + m_utime_delta = utime - m_prevutime; + m_prevutime = utime; + m_read_cpu = false; + } + + if (m_die) + { + return; + } + } + } + + int64_t get_tid() { return m_tid; } + + uint64_t m_prevutime; + uint64_t m_utime_delta; + std::atomic m_die; + std::atomic m_read_cpu; + int64_t m_tid; +}; + +TEST_F(sys_call_test, process_scap_proc_get) +{ + int callnum = 0; + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return m_tid_filter(evt); }; + + // + // TEST CODE + // + run_callback_t test = [](concurrent_object_handle inspector_handle) + { + usleep(1000); + + int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + EXPECT_LT(0, s); + + int s1 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + EXPECT_LT(0, s); + + usleep(1000000); + + close(s); + close(s1); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_SYSCALL_NANOSLEEP_E) + { + if (callnum == 0) + { + scap_threadinfo scap_proc; + + auto rc = + scap_proc_get(param.m_inspector->get_scap_platform(), 0, &scap_proc, false); + EXPECT_NE(SCAP_SUCCESS, rc); + + int64_t tid = e->get_tid(); + rc = scap_proc_get(param.m_inspector->get_scap_platform(), tid, &scap_proc, false); + EXPECT_EQ(SCAP_SUCCESS, rc); + } + else + { + scap_threadinfo scap_proc; + scap_fdinfo* fdi; + scap_fdinfo* tfdi; + uint32_t nsocks = 0; + int64_t tid = e->get_tid(); + + // + // try with scan_sockets=true + // + auto rc = + scap_proc_get(param.m_inspector->get_scap_platform(), tid, &scap_proc, false); + EXPECT_EQ(SCAP_SUCCESS, rc); + + HASH_ITER(hh, scap_proc.fdlist, fdi, tfdi) + { + if (fdi->type == SCAP_FD_IPV4_SOCK) + { + nsocks++; + } + } + + EXPECT_EQ(0U, nsocks); + + // + // try with scan_sockets=false + // + rc = scap_proc_get(param.m_inspector->get_scap_platform(), tid, &scap_proc, true); + EXPECT_EQ(SCAP_SUCCESS, rc); + + HASH_ITER(hh, scap_proc.fdlist, fdi, tfdi) + { + if (fdi->type == SCAP_FD_IPV4_SOCK) + { + nsocks++; + } + } + + EXPECT_EQ(0U, nsocks); + } + + callnum++; + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); +} + +TEST_F(sys_call_test, procinfo_processchild_cpuload) +{ + int callnum = 0; + int lastcpu = 0; + int64_t ctid = -1; + + loadthread ct; + std::thread th(&loadthread::run, std::ref(ct)); + + sleep(2); + ctid = ct.get_tid(); + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return true; }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + for (uint32_t j = 0; j < 5; j++) + { + sleep(1); + } + + ct.m_die = true; + + th.join(); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_PROCINFO_E) + { + sinsp_threadinfo* tinfo = e->get_thread_info(); + + if (tinfo) + { + if (tinfo->m_tid == ctid) + { + uint64_t tcpu; + + const sinsp_evt_param* parinfo = e->get_param(0); + //tcpu = *(uint64_t*)parinfo->m_val; + memcpy(&tcpu,parinfo->m_val, sizeof(uint64_t)); + + uint64_t delta = tcpu - lastcpu; + + ct.m_read_cpu = true; + + if (callnum != 0) + { + EXPECT_GT(delta, 0U); + EXPECT_LT(delta, 110U); + } + + lastcpu = tcpu; + + callnum++; + } + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); +} + +TEST_F(sys_call_test, procinfo_two_processchilds_cpuload) +{ + int callnum = 0; + int lastcpu = 0; + int lastcpu1 = 0; + + loadthread ct + ; + std::thread th(&loadthread::run, std::ref(ct)); + + loadthread ct1; + std::thread th1(&loadthread::run, std::ref(ct1)); + + sleep(2); + int64_t ctid = ct.get_tid(); + int64_t ctid1 = ct1.get_tid(); + + // + // FILTER + // + event_filter_t filter = [&](sinsp_evt* evt) { return true; }; + + // + // TEST CODE + // + run_callback_t test = [&](concurrent_object_handle inspector_handle) + { + for (uint32_t j = 0; j < 5; j++) + { + sleep(1); + } + + ct.m_die = true; + ct1.m_die = true; + + th.join(); + th1.join(); + }; + + // + // OUTPUT VALDATION + // + captured_event_callback_t callback = [&](const callback_param& param) + { + sinsp_evt* e = param.m_evt; + uint16_t type = e->get_type(); + + if (type == PPME_PROCINFO_E) + { + sinsp_threadinfo* tinfo = e->get_thread_info(); + + if (tinfo) + { + if (tinfo->m_tid == ctid) + { + uint64_t tcpu; + + const sinsp_evt_param* parinfo = e->get_param(0); + tcpu = *(uint64_t*)parinfo->m_val; + + uint64_t delta = tcpu - lastcpu; + + if (callnum > 2) + { + EXPECT_GT(delta, 0U); + EXPECT_LT(delta, 110U); + } + + lastcpu = tcpu; + + callnum++; + } + else if (tinfo->m_tid == ctid1) + { + uint64_t tcpu; + + const sinsp_evt_param* parinfo = e->get_param(0); + tcpu = *(uint64_t*)parinfo->m_val; + + uint64_t delta = tcpu - lastcpu1; + + if (callnum > 2) + { + EXPECT_GT(delta, 0U); + EXPECT_LT(delta, 110U); + } + + lastcpu1 = tcpu; + + callnum++; + } + } + } + }; + + ASSERT_NO_FATAL_FAILURE({ event_capture::run(test, callback, filter); }); +} diff --git a/test/libsinsp_e2e/threadinfo.cpp b/test/libsinsp_e2e/threadinfo.cpp new file mode 100644 index 00000000000..560b52d452b --- /dev/null +++ b/test/libsinsp_e2e/threadinfo.cpp @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2024 The Falco Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include +#include +#include + +#include +#include +#include + +class threadinfo_test : public testing::Test +{ +}; + +static void check_iov(struct iovec* iov, + int iovcnt, + std::string rem, + std::vector& expected, + std::string expectedrem) +{ + ASSERT_EQ((unsigned)iovcnt, expected.size()); + + for (int i = 0; i < iovcnt; i++) + { + ASSERT_EQ(iov[i].iov_len, expected[i].iov_len); + ASSERT_TRUE(memcmp(iov[i].iov_base, expected[i].iov_base, iov[i].iov_len) == 0); + } + + EXPECT_TRUE(rem == expectedrem); +} + +enum test_type +{ + TEST_ARGS = 0, + TEST_ENV = 1, + TEST_CGROUPS = 2 +}; + +static void run_test(test_type ttype, + std::vector& vals, + std::vector& expected, + std::string expectedrem) +{ + sinsp_threadinfo ti(nullptr); + struct iovec* iov; + int iovcnt; + std::string rem; + sinsp_threadinfo::cgroups_t cg; + + for (auto& val : vals) + { + switch (ttype) + { + case TEST_ARGS: + ti.m_args.push_back(val.c_str()); + break; + case TEST_ENV: + ti.m_env.push_back(val.c_str()); + break; + case TEST_CGROUPS: + size_t pos = val.find("="); + ASSERT_NE(pos, std::string::npos); + ti.cgroups().push_back(make_pair(val.substr(0, pos), val.substr(pos + 1))); + break; + } + } + + switch (ttype) + { + case TEST_ARGS: + ti.args_to_iovec(&iov, &iovcnt, rem); + break; + case TEST_ENV: + ti.env_to_iovec(&iov, &iovcnt, rem); + break; + case TEST_CGROUPS: + cg = ti.cgroups(); + ti.cgroups_to_iovec(&iov, &iovcnt, rem, cg); + break; + }; + + std::vector expected_iov; + for (auto& exp : expected) + { + if (ttype == TEST_ARGS || ttype == TEST_ENV) + { + // A trailing NULL is assumed for all values + expected_iov.emplace_back(iovec{(void*)exp.c_str(), exp.size() + 1}); + } + else + { + expected_iov.emplace_back(iovec{(void*)exp.data(), exp.size()}); + } + } + + check_iov(iov, iovcnt, rem, expected_iov, expectedrem); + + free(iov); +} + +TEST_F(threadinfo_test, args) +{ + std::vector args = {"-i", "206", "--switch", "f"}; + std::string expectedrem; + + run_test(TEST_ARGS, args, args, expectedrem); +} + +TEST_F(threadinfo_test, args_skip) +{ + std::string full(SCAP_MAX_ARGS_SIZE - 1, 'a'); + + std::vector args = {full, "will-be-skipped"}; + std::vector expected = {full}; + std::string expectedrem; + + run_test(TEST_ARGS, args, expected, expectedrem); +} + +TEST_F(threadinfo_test, argstrunc_single) +{ + std::string full(SCAP_MAX_ARGS_SIZE, 'a'); + std::string trunc(SCAP_MAX_ARGS_SIZE - 1, 'a'); + + std::vector args = {full, "will-be-skipped"}; + std::vector expected = {trunc}; + std::string expectedrem = trunc; + + run_test(TEST_ARGS, args, expected, expectedrem); +} + +TEST_F(threadinfo_test, argstrunc_multi) +{ + std::string full(SCAP_MAX_ARGS_SIZE, 'a'); + std::string trunc(SCAP_MAX_ARGS_SIZE - 6, 'a'); + + std::vector args = {"0123", full}; + std::vector expected = {"0123", trunc}; + std::string expectedrem = trunc; + + run_test(TEST_ARGS, args, expected, expectedrem); +} + +TEST_F(threadinfo_test, envs) +{ + std::vector envs = {"-i", "206", "--switch", "f"}; + std::string expectedrem; + + run_test(TEST_ENV, envs, envs, expectedrem); +} + +TEST_F(threadinfo_test, envs_skip) +{ + std::string full(SCAP_MAX_ENV_SIZE - 1, 'a'); + + std::vector envs = {full, "will-be-skipped"}; + std::vector expected = {full}; + std::string expectedrem; + + run_test(TEST_ENV, envs, expected, expectedrem); +} + +TEST_F(threadinfo_test, envstrunc_single) +{ + std::string full(SCAP_MAX_ENV_SIZE, 'a'); + std::string trunc(SCAP_MAX_ENV_SIZE - 1, 'a'); + + std::vector envs = {full, "will-be-skipped"}; + std::vector expected = {trunc}; + std::string expectedrem = trunc; + + run_test(TEST_ENV, envs, expected, expectedrem); +} + +TEST_F(threadinfo_test, envstrunc_multi) +{ + std::string full(SCAP_MAX_ENV_SIZE, 'a'); + std::string trunc(SCAP_MAX_ENV_SIZE - 6, 'a'); + + std::vector envs = {"0123", full}; + std::vector expected = {"0123", trunc}; + std::string expectedrem = trunc; + + run_test(TEST_ENV, envs, expected, expectedrem); +} + +TEST_F(threadinfo_test, cgroups) +{ + std::vector cgroups = { + "cpuset=/docker/875f9d8728e84761e4669b21acbf035b3a3fda62d7f6e35dd857781932cd74e8", + "perf_event=/docker/875f9d8728e84761e4669b21acbf035b3a3fda62d7f6e35dd857781932cd74e8", + "memory=/docker/875f9d8728e84761e4669b21acbf035b3a3fda62d7f6e35dd857781932cd74e8", + "rdma=/"}; + + std::vector expected = { + "cpuset", + "=", + "/docker/875f9d8728e84761e4669b21acbf035b3a3fda62d7f6e35dd857781932cd74e8", + "perf_event", + "=", + "/docker/875f9d8728e84761e4669b21acbf035b3a3fda62d7f6e35dd857781932cd74e8", + "memory", + "=", + "/docker/875f9d8728e84761e4669b21acbf035b3a3fda62d7f6e35dd857781932cd74e8", + "rdma", + "=", + "/"}; + + expected[2].push_back('\0'); + expected[5].push_back('\0'); + expected[8].push_back('\0'); + expected[11].push_back('\0'); + std::string expectedrem; + + run_test(TEST_CGROUPS, cgroups, expected, expectedrem); +} + +TEST_F(threadinfo_test, cgroups_skip) +{ + std::string full(SCAP_MAX_CGROUPS_SIZE - 8, 'a'); + + std::vector cgroups = {"cpuset=" + full, "rdma=will-be-skipped"}; + std::vector expected = {"cpuset", "=", full}; + expected[2].push_back('\0'); + std::string expectedrem; + + run_test(TEST_CGROUPS, cgroups, expected, expectedrem); +} + +TEST_F(threadinfo_test, cgroupstrunc_single) +{ + std::string full(SCAP_MAX_CGROUPS_SIZE - 7, 'a'); + std::string trunc(SCAP_MAX_CGROUPS_SIZE - 8, 'a'); + + std::vector cgroups = {"cpuset=" + full, "rdma=will-be-skipped"}; + std::vector expected = {"cpuset", "=", trunc}; + expected[2].push_back('\0'); + std::string expectedrem = trunc; + + run_test(TEST_CGROUPS, cgroups, expected, expectedrem); +} + +TEST_F(threadinfo_test, cgroupstrunc_multi) +{ + std::string full(SCAP_MAX_CGROUPS_SIZE, 'a'); + std::string trunc(SCAP_MAX_CGROUPS_SIZE - 15, 'a'); + + std::vector cgroups = {"cpuset=1", "rdma=" + full}; + std::vector expected = {"cpuset", "=", "1", "rdma", "=", trunc}; + expected[2].push_back('\0'); + expected[5].push_back('\0'); + std::string expectedrem = trunc; + + run_test(TEST_CGROUPS, cgroups, expected, expectedrem); +} + +TEST_F(threadinfo_test, cgroupstrunc_noeq) +{ + std::string full(SCAP_MAX_CGROUPS_SIZE, 'a'); + std::string trunc(SCAP_MAX_CGROUPS_SIZE - 10, 'a'); + + std::vector cgroups = {"cpuset=1", full + "=" + "1"}; + std::vector expected = {"cpuset", "=", "1", trunc}; + expected[2].push_back('\0'); + expected[3].push_back('\0'); + std::string expectedrem = trunc; + + run_test(TEST_CGROUPS, cgroups, expected, expectedrem); +}