diff --git a/userspace/libsinsp/test/CMakeLists.txt b/userspace/libsinsp/test/CMakeLists.txt index 5dd47c523c..6f7601bc04 100644 --- a/userspace/libsinsp/test/CMakeLists.txt +++ b/userspace/libsinsp/test/CMakeLists.txt @@ -83,6 +83,7 @@ file(GLOB_RECURSE TEST_HELPERS ${CMAKE_CURRENT_SOURCE_DIR}/helpers/*.cpp) set(LIBSINSP_UNIT_TESTS_SOURCES test_utils.cpp + sinsp_with_test_input.cpp cgroup_list_counter.ut.cpp events_evt.ut.cpp events_file.ut.cpp diff --git a/userspace/libsinsp/test/helpers/threads_helpers.h b/userspace/libsinsp/test/helpers/threads_helpers.h index f394662117..1e5c84824a 100644 --- a/userspace/libsinsp/test/helpers/threads_helpers.h +++ b/userspace/libsinsp/test/helpers/threads_helpers.h @@ -166,9 +166,9 @@ limitations under the License. \ /*=============================== p1_t1 ===========================*/ \ \ - UNUSED int64_t p1_t1_tid = 2; \ - UNUSED int64_t p1_t1_pid = p1_t1_tid; \ - UNUSED int64_t p1_t1_ptid = INIT_TID; \ + [[maybe_unused]] int64_t p1_t1_tid = 2; \ + [[maybe_unused]] int64_t p1_t1_pid = p1_t1_tid; \ + [[maybe_unused]] int64_t p1_t1_ptid = INIT_TID; \ \ /* Parent exit event */ \ generate_clone_x_event(p1_t1_tid, INIT_TID, INIT_PID, INIT_PTID); \ @@ -179,9 +179,9 @@ limitations under the License. \ /*=============================== p1_t2 ===========================*/ \ \ - UNUSED int64_t p1_t2_tid = 6; \ - UNUSED int64_t p1_t2_pid = p1_t1_pid; \ - UNUSED int64_t p1_t2_ptid = INIT_TID; \ + [[maybe_unused]] int64_t p1_t2_tid = 6; \ + [[maybe_unused]] int64_t p1_t2_pid = p1_t1_pid; \ + [[maybe_unused]] int64_t p1_t2_ptid = INIT_TID; \ \ /* Parent exit event */ \ generate_clone_x_event(p1_t2_tid, p1_t1_tid, p1_t1_pid, p1_t1_ptid, PPM_CL_CLONE_THREAD); \ @@ -192,9 +192,9 @@ limitations under the License. \ /*=============================== p2_t1 ===========================*/ \ \ - UNUSED int64_t p2_t1_tid = 25; \ - UNUSED int64_t p2_t1_pid = 25; \ - UNUSED int64_t p2_t1_ptid = INIT_TID; \ + [[maybe_unused]] int64_t p2_t1_tid = 25; \ + [[maybe_unused]] int64_t p2_t1_pid = 25; \ + [[maybe_unused]] int64_t p2_t1_ptid = INIT_TID; \ \ /* Parent exit event */ \ generate_clone_x_event(p2_t1_tid, p1_t2_tid, p1_t2_pid, p1_t2_ptid, PPM_CL_CLONE_PARENT); \ @@ -209,9 +209,9 @@ limitations under the License. \ /*=============================== p2_t2 ===========================*/ \ \ - UNUSED int64_t p2_t2_tid = 23; \ - UNUSED int64_t p2_t2_pid = p2_t1_pid; \ - UNUSED int64_t p2_t2_ptid = INIT_TID; /* p2_t2 will have the same parent of p2_t1 */ \ + [[maybe_unused]] int64_t p2_t2_tid = 23; \ + [[maybe_unused]] int64_t p2_t2_pid = p2_t1_pid; \ + [[maybe_unused]] int64_t p2_t2_ptid = INIT_TID; /* p2_t2 will have the same parent of p2_t1 */ \ \ /* Parent exit event */ \ generate_clone_x_event(p2_t2_tid, p2_t1_tid, p2_t1_pid, p2_t1_ptid, PPM_CL_CLONE_THREAD); \ @@ -222,9 +222,9 @@ limitations under the License. \ /*=============================== p2_t3 ===========================*/ \ \ - UNUSED int64_t p2_t3_tid = 24; \ - UNUSED int64_t p2_t3_pid = p2_t1_pid; \ - UNUSED int64_t p2_t3_ptid = INIT_TID; \ + [[maybe_unused]] int64_t p2_t3_tid = 24; \ + [[maybe_unused]] int64_t p2_t3_pid = p2_t1_pid; \ + [[maybe_unused]] int64_t p2_t3_ptid = INIT_TID; \ \ /* Parent exit event */ \ generate_clone_x_event(p2_t3_tid, p2_t2_tid, p2_t2_pid, p2_t2_ptid, PPM_CL_CLONE_THREAD); \ @@ -235,9 +235,9 @@ limitations under the License. \ /*=============================== p3_t1 ===========================*/ \ \ - UNUSED int64_t p3_t1_tid = 72; \ - UNUSED int64_t p3_t1_pid = p3_t1_tid; \ - UNUSED int64_t p3_t1_ptid = p2_t1_tid; \ + [[maybe_unused]] int64_t p3_t1_tid = 72; \ + [[maybe_unused]] int64_t p3_t1_pid = p3_t1_tid; \ + [[maybe_unused]] int64_t p3_t1_ptid = p2_t1_tid; \ \ /* Parent exit event */ \ generate_clone_x_event(p3_t1_tid, p2_t1_tid, p2_t1_pid, p2_t1_ptid); \ @@ -248,11 +248,11 @@ limitations under the License. \ /*=============================== p4_t1 ===========================*/ \ \ - UNUSED int64_t p4_t1_tid = 76; \ - UNUSED int64_t p4_t1_pid = p4_t1_tid; \ - UNUSED int64_t p4_t1_ptid = p3_t1_tid; \ - UNUSED int64_t p4_t1_vtid = 1; /* This process will be the `init` one in the new namespace */ \ - UNUSED int64_t p4_t1_vpid = p4_t1_vtid; \ + [[maybe_unused]] int64_t p4_t1_tid = 76; \ + [[maybe_unused]] int64_t p4_t1_pid = p4_t1_tid; \ + [[maybe_unused]] int64_t p4_t1_ptid = p3_t1_tid; \ + [[maybe_unused]] int64_t p4_t1_vtid = 1; /* This process will be the `init` one in the new namespace */ \ + [[maybe_unused]] int64_t p4_t1_vpid = p4_t1_vtid; \ \ generate_clone_x_event(p4_t1_tid, p3_t1_tid, p3_t1_pid, p3_t1_ptid, PPM_CL_CLONE_NEWPID); \ \ @@ -270,11 +270,11 @@ limitations under the License. \ /*=============================== p4_t2 ===========================*/ \ \ - UNUSED int64_t p4_t2_tid = 79; \ - UNUSED int64_t p4_t2_pid = p4_t1_pid; \ - UNUSED int64_t p4_t2_ptid = p3_t1_tid; \ - UNUSED int64_t p4_t2_vtid = 2; \ - UNUSED int64_t p4_t2_vpid = p4_t1_vpid; \ + [[maybe_unused]] int64_t p4_t2_tid = 79; \ + [[maybe_unused]] int64_t p4_t2_pid = p4_t1_pid; \ + [[maybe_unused]] int64_t p4_t2_ptid = p3_t1_tid; \ + [[maybe_unused]] int64_t p4_t2_vtid = 2; \ + [[maybe_unused]] int64_t p4_t2_vpid = p4_t1_vpid; \ \ generate_clone_x_event(0, p4_t2_tid, p4_t2_pid, p4_t2_ptid, PPM_CL_CLONE_THREAD | PPM_CL_CHILD_IN_PIDNS, \ p4_t2_vtid, p4_t2_vpid); \ @@ -283,11 +283,11 @@ limitations under the License. \ /*=============================== p5_t1 ===========================*/ \ \ - UNUSED int64_t p5_t1_tid = 82; \ - UNUSED int64_t p5_t1_pid = p5_t1_tid; \ - UNUSED int64_t p5_t1_ptid = p4_t2_tid; \ - UNUSED int64_t p5_t1_vtid = 10; \ - UNUSED int64_t p5_t1_vpid = p5_t1_vtid; \ + [[maybe_unused]] int64_t p5_t1_tid = 82; \ + [[maybe_unused]] int64_t p5_t1_pid = p5_t1_tid; \ + [[maybe_unused]] int64_t p5_t1_ptid = p4_t2_tid; \ + [[maybe_unused]] int64_t p5_t1_vtid = 10; \ + [[maybe_unused]] int64_t p5_t1_vpid = p5_t1_vtid; \ \ generate_clone_x_event(0, p5_t1_tid, p5_t1_pid, p5_t1_ptid, PPM_CL_CHILD_IN_PIDNS, p5_t1_vtid, p5_t1_vpid); \ \ @@ -295,11 +295,11 @@ limitations under the License. \ /*=============================== p5_t2 ===========================*/ \ \ - UNUSED int64_t p5_t2_tid = 84; \ - UNUSED int64_t p5_t2_pid = p5_t1_pid; \ - UNUSED int64_t p5_t2_ptid = p4_t2_tid; \ - UNUSED int64_t p5_t2_vtid = 12; \ - UNUSED int64_t p5_t2_vpid = p5_t1_vpid; \ + [[maybe_unused]] int64_t p5_t2_tid = 84; \ + [[maybe_unused]] int64_t p5_t2_pid = p5_t1_pid; \ + [[maybe_unused]] int64_t p5_t2_ptid = p4_t2_tid; \ + [[maybe_unused]] int64_t p5_t2_vtid = 12; \ + [[maybe_unused]] int64_t p5_t2_vpid = p5_t1_vpid; \ \ generate_clone_x_event(0, p5_t2_tid, p5_t2_pid, p5_t2_ptid, PPM_CL_CHILD_IN_PIDNS, p5_t2_vtid, p5_t2_vpid); \ \ @@ -307,11 +307,11 @@ limitations under the License. \ /*=============================== p6_t1 ===========================*/ \ \ - UNUSED int64_t p6_t1_tid = 87; \ - UNUSED int64_t p6_t1_pid = p6_t1_tid; \ - UNUSED int64_t p6_t1_ptid = p5_t2_tid; \ - UNUSED int64_t p6_t1_vtid = 17; \ - UNUSED int64_t p6_t1_vpid = p6_t1_vtid; \ + [[maybe_unused]] int64_t p6_t1_tid = 87; \ + [[maybe_unused]] int64_t p6_t1_pid = p6_t1_tid; \ + [[maybe_unused]] int64_t p6_t1_ptid = p5_t2_tid; \ + [[maybe_unused]] int64_t p6_t1_vtid = 17; \ + [[maybe_unused]] int64_t p6_t1_vpid = p6_t1_vtid; \ \ generate_clone_x_event(0, p6_t1_tid, p6_t1_pid, p6_t1_ptid, PPM_CL_CHILD_IN_PIDNS, p6_t1_vtid, p6_t1_vpid); \ \ diff --git a/userspace/libsinsp/test/parsers/parse_clone.cpp b/userspace/libsinsp/test/parsers/parse_clone.cpp index 928524301d..070496cc3b 100644 --- a/userspace/libsinsp/test/parsers/parse_clone.cpp +++ b/userspace/libsinsp/test/parsers/parse_clone.cpp @@ -362,8 +362,8 @@ TEST_F(sinsp_with_test_input, CLONE_CALLER_comm_update) */ int64_t p2_t1_tid = 26; - UNUSED int64_t p2_t1_pid = 26; - UNUSED int64_t p2_t1_ptid = p1_t1_tid; + [[maybe_unused]] int64_t p2_t1_pid = 26; + [[maybe_unused]] int64_t p2_t1_ptid = p1_t1_tid; ASSERT_THREAD_INFO_COMM(p1_t1_tid, "old-name"); generate_clone_x_event(p2_t1_tid, p1_t1_tid, p1_t1_pid, p1_t1_ptid, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, @@ -440,7 +440,7 @@ TEST_F(sinsp_with_test_input, CLONE_CHILD_tid_collision) /* Create a mock child with a clone exit parent event */ int64_t p1_t1_tid = 24; - UNUSED int64_t p1_t1_pid = 24; + [[maybe_unused]] int64_t p1_t1_pid = 24; int64_t p1_t1_ptid = INIT_PID; /* Parent clone exit event */ @@ -609,8 +609,8 @@ TEST_F(sinsp_with_test_input, CLONE_CHILD_missing_both_clone_events_create_secon /* Init creates a new process p1 but we miss both clone events so we know nothing about it */ int64_t p1_t1_tid = 24; - UNUSED int64_t p1_t1_pid = 24; - UNUSED int64_t p1_t1_ptid = INIT_TID; + [[maybe_unused]] int64_t p1_t1_pid = 24; + [[maybe_unused]] int64_t p1_t1_ptid = INIT_TID; /* The process p1 creates a second thread p1_t2 */ int64_t p1_t2_tid = 30; diff --git a/userspace/libsinsp/test/parsers/parse_execve.cpp b/userspace/libsinsp/test/parsers/parse_execve.cpp index 9f7ede0b01..c913317a5b 100644 --- a/userspace/libsinsp/test/parsers/parse_execve.cpp +++ b/userspace/libsinsp/test/parsers/parse_execve.cpp @@ -59,8 +59,8 @@ TEST_F(sinsp_with_test_input, EXECVE_from_a_not_leader_thread_with_a_child) /* Create a child for `p2_t3` */ int64_t p7_t1_tid = 100; - UNUSED int64_t p7_t1_pid = 100; - UNUSED int64_t p7_t1_ptid = p2_t3_tid; + [[maybe_unused]] int64_t p7_t1_pid = 100; + [[maybe_unused]] int64_t p7_t1_ptid = p2_t3_tid; generate_clone_x_event(p7_t1_tid, p2_t3_tid, p2_t3_pid, p2_t3_ptid); ASSERT_THREAD_CHILDREN(p2_t3_tid, 1, 1, p7_t1_tid); @@ -178,7 +178,7 @@ TEST_F(sinsp_with_test_input, EXECVE_exepath_without_trusted_exepath) int64_t retval = 0; int64_t old_tid = p6_t1_tid; int64_t new_tid = p6_t1_tid; - int64_t pid = p6_t1_pid; + int64_t pid = p6_t1_pid; int64_t ppid = p6_t1_ptid; std::string pathname = "/bin/test-exe"; std::string comm = "test-exe"; diff --git a/userspace/libsinsp/test/sinsp_with_test_input.cpp b/userspace/libsinsp/test/sinsp_with_test_input.cpp new file mode 100644 index 0000000000..c4eaa7526e --- /dev/null +++ b/userspace/libsinsp/test/sinsp_with_test_input.cpp @@ -0,0 +1,495 @@ +// SPDX-License-Identifier: Apache-2.0 +/* +Copyright (C) 2023 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 "sinsp_with_test_input.h" + +sinsp_with_test_input::sinsp_with_test_input() +{ + m_test_data.event_count = 0; + m_test_data.events = nullptr; + m_test_data.thread_count = 0; + m_test_data.threads = nullptr; +} + +sinsp_with_test_input::~sinsp_with_test_input() +{ + for (auto& el : m_events) + { + free(el); + } + + for (auto& el : m_async_events) + { + free(el); + } +} + +void sinsp_with_test_input::open_inspector(sinsp_mode_t mode) { + m_inspector.open_test_input(&m_test_data, mode); +} + +scap_evt* sinsp_with_test_input::add_event(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, ...) +{ + va_list args; + va_start(args, n); + scap_evt* ret = add_event_v(ts, tid, event_type, n, args); + va_end(args); + + return ret; +} + +sinsp_evt* sinsp_with_test_input::advance_ts_get_event(uint64_t ts) +{ + for (sinsp_evt* evt = next_event(); evt != nullptr; evt = next_event()) { + if (evt->get_ts() == ts) { + return evt; + } + } + + return nullptr; +} + +// adds an event and advances the inspector to the new timestamp +sinsp_evt* sinsp_with_test_input::add_event_advance_ts(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, ...) +{ + va_list args; + va_start(args, n); + sinsp_evt* ret = add_event_advance_ts_v(ts, tid, event_type, n, args); + va_end(args); + + return ret; +} + +sinsp_evt* sinsp_with_test_input::add_event_advance_ts_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) +{ + add_event_v(ts, tid, event_type, n, args); + sinsp_evt* evt = advance_ts_get_event(ts); + if (evt != nullptr) { + return evt; + } + + throw std::runtime_error("could not retrieve last event or internal error (event vector size: " + std::to_string(m_events.size()) + std::string(")")); +} + +// Generates and allocates a new event. +scap_evt* sinsp_with_test_input::create_event_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) +{ + struct scap_sized_buffer event_buf = {NULL, 0}; + size_t event_size = 0; + char error[SCAP_LASTERR_SIZE] = {'\0'}; + va_list args2; + va_copy(args2, args); + + int32_t ret = scap_event_encode_params_v(event_buf, &event_size, error, event_type, n, args); + + if(ret != SCAP_INPUT_TOO_SMALL) { + va_end(args2); + return nullptr; + } + + event_buf.buf = malloc(event_size); + event_buf.size = event_size; + + if(event_buf.buf == NULL) { + va_end(args2); + return nullptr; + } + + ret = scap_event_encode_params_v(event_buf, &event_size, error, event_type, n, args2); + + if(ret != SCAP_SUCCESS) { + free(event_buf.buf); + event_buf.size = 0; + va_end(args2); + return nullptr; + } + + scap_evt* event = static_cast(event_buf.buf); + event->ts = ts; + event->tid = tid; + + va_end(args2); + return event; +} + +scap_evt* sinsp_with_test_input::add_event_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) +{ + if (ts < m_last_recorded_timestamp) { + throw std::runtime_error("the test framework does not currently support out of order events with decreasing timestamps"); + } + + scap_evt* event = create_event_v(ts, tid, event_type, n, args); + + uint64_t evtoffset = m_events.size() - m_test_data.event_count; + m_events.push_back(event); + m_test_data.events = m_events.data() + evtoffset; + m_test_data.event_count = m_events.size() - evtoffset; + m_last_recorded_timestamp = ts; + + return event; +} + +scap_evt* sinsp_with_test_input::add_async_event(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, ...) +{ + va_list args; + va_start(args, n); + scap_evt* ret = add_async_event_v(ts, tid, event_type, n, args); + va_end(args); + + return ret; +} + +scap_evt* sinsp_with_test_input::add_async_event_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) +{ + scap_evt* scap_event = create_event_v(ts, tid, event_type, n, args); + m_async_events.push_back(scap_event); + + auto event = std::make_unique(); + event->set_scap_evt(scap_event); + event->set_cpuid(0); + event->get_scap_evt()->ts = ts; + m_inspector.handle_async_event(std::move(event)); + + return scap_event; +} + +//=============================== PROCESS GENERATION =========================== + +// Allowed event types: PPME_SYSCALL_CLONE_20_X, PPME_SYSCALL_FORK_20_X, PPME_SYSCALL_VFORK_20_X, PPME_SYSCALL_CLONE3_X +sinsp_evt* sinsp_with_test_input::generate_clone_x_event(int64_t retval, int64_t tid, int64_t pid, int64_t ppid, uint32_t flags, + int64_t vtid, int64_t vpid, + const std::string& name, const std::vector& cgroup_vec, + ppm_event_code event_type) +{ + if(vtid == DEFAULT_VALUE) + { + vtid = tid; + } + + if(vpid == DEFAULT_VALUE) + { + vpid = pid; + } + + // Scaffolding needed to call the PPME_SYSCALL_CLONE_20_X + uint64_t not_relevant_64 = 0; + uint32_t not_relevant_32 = 0; + + scap_const_sized_buffer empty_bytebuf = {/*.buf =*/nullptr, /*.size =*/0}; + scap_const_sized_buffer cgroup_byte_buf = empty_bytebuf; + std::string cgroupsv = test_utils::to_null_delimited(cgroup_vec); + + // If the cgroup vector is not empty overwrite it + if(!cgroup_vec.empty()) + { + cgroup_byte_buf = scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}; + } + + return add_event_advance_ts(increasing_ts(), tid, event_type, 20, retval, name.c_str(), empty_bytebuf, + tid, pid, ppid, "", not_relevant_64, not_relevant_64, not_relevant_64, + not_relevant_32, not_relevant_32, not_relevant_32, name.c_str(), + cgroup_byte_buf, flags, not_relevant_32, not_relevant_32, vtid, vpid); +} + +sinsp_evt* sinsp_with_test_input::generate_execve_enter_and_exit_event(int64_t retval, int64_t old_tid, int64_t new_tid, int64_t pid, + int64_t ppid, const std::string& pathname, + const std::string& comm, + const std::string& resolved_kernel_path, + const std::vector& cgroup_vec) +{ + // Scaffolding needed to call the PPME_SYSCALL_EXECVE_19_X + uint64_t not_relevant_64 = 0; + uint32_t not_relevant_32 = 0; + scap_const_sized_buffer empty_bytebuf = { /*.buf =*/nullptr, /*.size =*/0 }; + scap_const_sized_buffer cgroup_byte_buf = empty_bytebuf; + std::string cgroupsv = test_utils::to_null_delimited(cgroup_vec); + + // If the cgroup vector is not empty overwrite it + if(!cgroup_vec.empty()) + { + cgroup_byte_buf = scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}; + } + + add_event_advance_ts(increasing_ts(), old_tid, PPME_SYSCALL_EXECVE_19_E, 1, pathname.c_str()); + // we have an `old_tid` and a `new_tid` because if a secondary thread calls the execve + // the thread leader will take control so the `tid` between enter and exit event will change + return add_event_advance_ts( + increasing_ts(), new_tid, PPME_SYSCALL_EXECVE_19_X, 28, retval, pathname.c_str(), empty_bytebuf, + new_tid, pid, ppid, "", not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_32, + not_relevant_32, not_relevant_32, comm.c_str(), cgroup_byte_buf, empty_bytebuf, not_relevant_32, + not_relevant_64, not_relevant_32, not_relevant_32, not_relevant_64, not_relevant_64, + not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_32, + resolved_kernel_path.c_str()); +} + +void sinsp_with_test_input::remove_thread(int64_t tid_to_remove, int64_t reaper_tid) +{ + generate_proc_exit_event(tid_to_remove, reaper_tid); + // Generate a random event on init to trigger the removal after proc exit + generate_random_event(); +} + +sinsp_evt* sinsp_with_test_input::generate_proc_exit_event(int64_t tid_to_remove, int64_t reaper_tid) +{ + // Scaffolding needed to call the PPME_PROCEXIT_1_E + int64_t not_relevant_64 = 0; + uint8_t not_relevant_8 = 0; + + return add_event_advance_ts(increasing_ts(), tid_to_remove, PPME_PROCEXIT_1_E, 5, not_relevant_64, not_relevant_64, not_relevant_8, not_relevant_8, reaper_tid); +} + +sinsp_evt* sinsp_with_test_input::generate_random_event(int64_t tid_caller) +{ + // Generate a random event on init to trigger the removal after proc exit + return add_event_advance_ts(increasing_ts(), tid_caller, PPME_SYSCALL_GETCWD_E, 0); +} + +//=============================== PROCESS GENERATION =========================== + +void sinsp_with_test_input::add_thread(const scap_threadinfo& tinfo, const std::vector& fdinfos) +{ + m_threads.push_back(tinfo); + m_test_data.threads = m_threads.data(); + m_test_data.thread_count = m_threads.size(); + + m_fdinfos.push_back(fdinfos); + scap_test_fdinfo_data fdinfo_descriptor = { + /*.fdinfos =*/ m_fdinfos.back().data(), + /*.fdinfo_count =*/ m_fdinfos.back().size() + }; + m_test_fdinfo_data.push_back(fdinfo_descriptor); + + m_test_data.fdinfo_data = m_test_fdinfo_data.data(); +} + +void sinsp_with_test_input::set_threadinfo_last_access_time(int64_t tid, uint64_t access_time_ns) +{ + auto tinfo = m_inspector.get_thread_ref(tid, false).get(); + if(tinfo != nullptr) + { + tinfo->m_lastaccess_ts = access_time_ns; + } + else + { + throw sinsp_exception("There is no thread info associated with tid: " + std::to_string(tid)); + } +} + +// Remove all threads with `tinfo->m_lastaccess_ts` minor than `m_lastevent_ts - thread_timeout` +void sinsp_with_test_input::remove_inactive_threads(uint64_t m_lastevent_ts, uint64_t thread_timeout) +{ + // We need to set these 2 variables to enable the remove_inactive_logic + m_inspector.m_thread_manager->set_last_flush_time_ns(1); + m_inspector.m_threads_purging_scan_time_ns = 2; + + m_inspector.set_lastevent_ts(m_lastevent_ts); + m_inspector.m_thread_timeout_ns = thread_timeout; + m_inspector.remove_inactive_threads(); +} + +// static +scap_threadinfo sinsp_with_test_input::create_threadinfo( + uint64_t tid, uint64_t pid, uint64_t ptid, uint64_t vpgid, int64_t vtid, int64_t vpid, + const std::string& comm, const std::string& exe, const std::string& exepath, + uint64_t clone_ts, uint32_t uid, uint32_t gid, + const std::vector& args, uint64_t sid, + const std::vector& env, const std::string& cwd, + int64_t fdlimit, uint32_t flags, bool exe_writable, + uint64_t cap_permitted, uint64_t cap_inheritable, uint64_t cap_effective, + uint32_t vmsize_kb, uint32_t vmrss_kb, uint32_t vmswap_kb, uint64_t pfmajor, uint64_t pfminor, + const std::vector& cgroups, const std::string& root, + int filtered_out, uint32_t tty, uint32_t loginuid) +{ + scap_threadinfo tinfo; + tinfo.tid = tid; + tinfo.pid = pid; + tinfo.ptid = ptid; + tinfo.sid = sid; + tinfo.vpgid = vpgid; + tinfo.exe_writable = exe_writable; + tinfo.fdlimit = fdlimit; + tinfo.flags = flags; + tinfo.uid = uid; + tinfo.gid = gid; + tinfo.cap_permitted = cap_permitted; + tinfo.cap_effective = cap_effective; + tinfo.cap_inheritable = cap_inheritable; + tinfo.vmsize_kb = vmsize_kb; + tinfo.vmrss_kb = vmrss_kb; + tinfo.pfmajor = pfmajor; + tinfo.pfminor = pfminor; + tinfo.vtid = vtid; + tinfo.vpid = vpid; + tinfo.filtered_out = filtered_out; + tinfo.fdlist = nullptr; + tinfo.clone_ts = clone_ts; + tinfo.tty = tty; + tinfo.loginuid = loginuid; + + std::string argsv; + if (!args.empty()) + { + argsv = test_utils::to_null_delimited(args); + argsv.push_back('\0'); + } + + std::string envv; + if (!env.empty()) + { + envv = test_utils::to_null_delimited(env); + envv.push_back('\0'); + } + + std::string cgroupsv; + if (!cgroups.empty()) + { + cgroupsv = test_utils::to_null_delimited(cgroups); + cgroupsv.push_back('\0'); + } + + memcpy(tinfo.args, argsv.data(), argsv.size()); + tinfo.args_len = argsv.size(); + memcpy(tinfo.env, envv.data(), envv.size()); + tinfo.env_len = envv.size(); + memcpy(tinfo.cgroups.path, cgroupsv.data(), cgroupsv.size()); + tinfo.cgroups.len = cgroupsv.size(); + + strlcpy(tinfo.cwd, cwd.c_str(), sizeof(tinfo.cwd)); + strlcpy(tinfo.comm, comm.c_str(), sizeof(tinfo.comm)); + strlcpy(tinfo.exe, exe.c_str(), sizeof(tinfo.exe)); + strlcpy(tinfo.exepath, exepath.c_str(), sizeof(tinfo.exepath)); + strlcpy(tinfo.root, root.c_str(), sizeof(tinfo.root)); + return tinfo; +} + +void sinsp_with_test_input::add_default_init_thread() +{ + std::vector env = { "TEST_ENV_PARENT_LINEAGE=secret", "HOME=/home/user/parent" }; + scap_threadinfo tinfo = create_threadinfo(1, 1, 0, 1, 1, 1, "init", "/sbin/init", "/sbin/init", increasing_ts(), 0, 0, {}, 0, env, "/root/"); + + std::vector fdinfos; + scap_fdinfo fdinfo; + fdinfo.fd = 0; + fdinfo.ino = 5; + fdinfo.type = SCAP_FD_FILE_V2; + + fdinfo.info.regularinfo.open_flags = PPM_O_RDONLY; + fdinfo.info.regularinfo.mount_id = 25; + fdinfo.info.regularinfo.dev = 0; + strlcpy(fdinfo.info.regularinfo.fname, "/dev/null", sizeof(fdinfo.info.regularinfo.fname)); + + fdinfos.push_back(fdinfo); + + add_thread(tinfo, fdinfos); +} + +void sinsp_with_test_input::add_simple_thread(int64_t tid, int64_t pid, int64_t ptid, const std::string& comm) +{ + scap_threadinfo tinfo = create_threadinfo(tid, pid, ptid, tid, tid, pid, comm, "/sbin/init", "/sbin/init", increasing_ts(), 0, 0, {}, 0, {}, "/root/"); + add_thread(tinfo, {}); +} + +uint64_t sinsp_with_test_input::increasing_ts() +{ + uint64_t ret = m_test_timestamp; + m_test_timestamp += 10000000; // 10 msec increment + return ret; +} + +// Return true if `field_name` exists in the filtercheck list. +// The field value could also be NULL, but in this method, we are not interested in the value. +bool sinsp_with_test_input::field_exists(sinsp_evt* evt, const std::string& field_name) +{ + return field_exists(evt, field_name, m_default_filterlist); +} + +bool sinsp_with_test_input::field_exists(sinsp_evt* evt, const std::string& field_name, filter_check_list& flist) +{ + if (evt == nullptr) { + throw sinsp_exception("The event class is NULL"); + } + + auto new_fl = flist.new_filter_check_from_fldname(field_name, &m_inspector, false); + if(new_fl != nullptr) + { + // if we can create a filter check it means that the field exists + delete new_fl; + return true; + } + else + { + return false; + } +} + +// Return true if `field_name` value is not NULL for this event. +bool sinsp_with_test_input::field_has_value(sinsp_evt* evt, const std::string& field_name) +{ + return field_has_value(evt, field_name, m_default_filterlist); +} + +bool sinsp_with_test_input::field_has_value(sinsp_evt* evt, const std::string& field_name, filter_check_list& flist) +{ + if (evt == nullptr) { + throw sinsp_exception("The event class is NULL"); + } + + std::unique_ptr chk(flist.new_filter_check_from_fldname(field_name, &m_inspector, false)); + if(chk == nullptr) + { + throw sinsp_exception("The field " + field_name + " is not a valid field."); + } + // we created a filter check starting from the field name so if we arrive here we will find it for sure + chk->parse_field_name(field_name.c_str(), true, false); + std::vector values; + return chk->extract(evt, values); +} + +std::string sinsp_with_test_input::get_field_as_string(sinsp_evt* evt, const std::string& field_name) +{ + return get_field_as_string(evt, field_name, m_default_filterlist); +} + +std::string sinsp_with_test_input::get_field_as_string(sinsp_evt* evt, const std::string& field_name, filter_check_list& flist) +{ + if (evt == nullptr) { + throw sinsp_exception("The event class is NULL"); + } + + std::unique_ptr chk(flist.new_filter_check_from_fldname(field_name, &m_inspector, false)); + if(chk == nullptr) + { + throw sinsp_exception("The field " + field_name + " is not a valid field."); + } + // we created a filter check starting from the field name so if we arrive here we will find it for sure + chk->parse_field_name(field_name.c_str(), true, false); + + const char* result = chk->tostring(evt); + if (result == nullptr) { + throw sinsp_exception("The field " + field_name + " is NULL"); + } + + return result; +} + +sinsp_evt* sinsp_with_test_input::next_event() +{ + sinsp_evt* evt; + auto result = m_inspector.next(&evt); + return result == SCAP_SUCCESS ? evt : nullptr; +} diff --git a/userspace/libsinsp/test/sinsp_with_test_input.h b/userspace/libsinsp/test/sinsp_with_test_input.h index 1342b45fc4..3816984776 100644 --- a/userspace/libsinsp/test/sinsp_with_test_input.h +++ b/userspace/libsinsp/test/sinsp_with_test_input.h @@ -18,507 +18,86 @@ limitations under the License. #pragma once -#include -#include +#include "test_utils.h" #include #include #include #include -#include "test_utils.h" #include +#include +#include + #define DEFAULT_VALUE 0 #define INIT_TID 1 #define INIT_PID INIT_TID #define INIT_PTID 0 -#define UNUSED __attribute__((unused)) -class sinsp_with_test_input : public ::testing::Test { +class sinsp_with_test_input : public ::testing::Test +{ protected: - void SetUp() override - { - m_test_data = std::unique_ptr(new scap_test_input_data); - m_test_data->event_count = 0; - m_test_data->events = nullptr; - m_test_data->thread_count = 0; - m_test_data->threads = nullptr; - - m_test_timestamp = 1566230400000000000; - m_last_recorded_timestamp = 0; - } - - void TearDown() override - { - for (size_t i = 0; i < m_events.size(); i++) - { - free(m_events[i]); - } - - for (size_t i = 0; i < m_async_events.size(); i++) - { - free(m_async_events[i]); - } - } + sinsp_with_test_input(); + ~sinsp_with_test_input(); sinsp m_inspector; - void open_inspector(sinsp_mode_t mode = SINSP_MODE_TEST) { - m_inspector.open_test_input(m_test_data.get(), mode); - } - - scap_evt* add_event(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, ...) - { - va_list args; - va_start(args, n); - scap_evt *ret = add_event_v(ts, tid, event_type, n, args); - va_end(args); - - return ret; - } - - sinsp_evt* advance_ts_get_event(uint64_t ts) - { - sinsp_evt *sinsp_event; - for (sinsp_event = next_event(); sinsp_event != nullptr; sinsp_event = next_event()) { - if (sinsp_event->get_ts() == ts) { - return sinsp_event; - } - } - - return nullptr; - } - - // adds an event and advances the inspector to the new timestamp - sinsp_evt* add_event_advance_ts(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, ...) - { - va_list args; - va_start(args, n); - sinsp_evt *ret = add_event_advance_ts_v(ts, tid, event_type, n, args); - va_end(args); - - return ret; - } - - sinsp_evt* add_event_advance_ts_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) - { - add_event_v(ts, tid, event_type, n, args); - sinsp_evt *sinsp_event = advance_ts_get_event(ts); - if (sinsp_event != nullptr) { - return sinsp_event; - } - - throw std::runtime_error("could not retrieve last event or internal error (event vector size: " + std::to_string(m_events.size()) + std::string(")")); - } - - // Generates and allocates a new event. - scap_evt* create_event_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) - { - struct scap_sized_buffer event_buf = {NULL, 0}; - size_t event_size = 0; - char error[SCAP_LASTERR_SIZE] = {'\0'}; - va_list args2; - va_copy(args2, args); - - int32_t ret = scap_event_encode_params_v(event_buf, &event_size, error, event_type, n, args); + void open_inspector(sinsp_mode_t mode = SINSP_MODE_TEST); + scap_evt* add_event(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, ...); + sinsp_evt* advance_ts_get_event(uint64_t ts); + sinsp_evt* add_event_advance_ts(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, ...); + sinsp_evt* add_event_advance_ts_v(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, va_list args); + scap_evt* create_event_v(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, va_list args); + scap_evt* add_event_v(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, va_list args); + scap_evt* add_async_event(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, ...); + scap_evt* add_async_event_v(uint64_t ts, uint64_t tid, ppm_event_code, uint32_t n, va_list args); - if(ret != SCAP_INPUT_TOO_SMALL) { - va_end(args2); - return nullptr; - } - - event_buf.buf = malloc(event_size); - event_buf.size = event_size; - - if(event_buf.buf == NULL) { - va_end(args2); - return nullptr; - } - - ret = scap_event_encode_params_v(event_buf, &event_size, error, event_type, n, args2); - - if(ret != SCAP_SUCCESS) { - free(event_buf.buf); - event_buf.size = 0; - va_end(args2); - return nullptr; - } - - scap_evt *event = static_cast(event_buf.buf); - event->ts = ts; - event->tid = tid; - - va_end(args2); - return event; - } - - scap_evt* add_event_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) - { - if (ts < m_last_recorded_timestamp) { - throw std::runtime_error("the test framework does not currently support out of order events with decreasing timestamps"); - } - - scap_evt *event = create_event_v(ts, tid, event_type, n, args); - - uint64_t evtoffset = m_events.size() - m_test_data->event_count; - m_events.push_back(event); - m_test_data->events = m_events.data() + evtoffset; - m_test_data->event_count = m_events.size() - evtoffset; - m_last_recorded_timestamp = ts; - - return event; - } - - scap_evt* add_async_event(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, ...) - { - va_list args; - va_start(args, n); - scap_evt *ret = add_async_event_v(ts, tid, event_type, n, args); - va_end(args); - - return ret; - } - - scap_evt* add_async_event_v(uint64_t ts, uint64_t tid, ppm_event_code event_type, uint32_t n, va_list args) - { - scap_evt *scap_event = create_event_v(ts, tid, event_type, n, args); - m_async_events.push_back(scap_event); - - auto event = std::make_unique(); - event->set_scap_evt(scap_event); - event->set_cpuid(0); - event->get_scap_evt()->ts = ts; - m_inspector.handle_async_event(std::move(event)); - - return scap_event; - } - - /*=============================== PROCESS GENERATION ===========================*/ + //=============================== PROCESS GENERATION =========================== // Allowed event types: PPME_SYSCALL_CLONE_20_X, PPME_SYSCALL_FORK_20_X, PPME_SYSCALL_VFORK_20_X, PPME_SYSCALL_CLONE3_X sinsp_evt* generate_clone_x_event(int64_t retval, int64_t tid, int64_t pid, int64_t ppid, uint32_t flags = 0, int64_t vtid = DEFAULT_VALUE, int64_t vpid = DEFAULT_VALUE, - std::string name = "bash", std::vector cgroup_vec = {}, - ppm_event_code event_type = PPME_SYSCALL_CLONE_20_X) - { - if(vtid == DEFAULT_VALUE) - { - vtid = tid; - } - - if(vpid == DEFAULT_VALUE) - { - vpid = pid; - } - - /* Scaffolding needed to call the PPME_SYSCALL_CLONE_20_X */ - uint64_t not_relevant_64 = 0; - uint32_t not_relevant_32 = 0; - - scap_const_sized_buffer empty_bytebuf = {/*.buf =*/nullptr, /*.size =*/0}; - scap_const_sized_buffer cgroup_byte_buf = empty_bytebuf; - std::string cgroupsv = test_utils::to_null_delimited(cgroup_vec); - - /* If the cgroup vector is not empty overwrite it */ - if(!cgroup_vec.empty()) - { - cgroup_byte_buf = scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}; - } - - return add_event_advance_ts(increasing_ts(), tid, event_type, 20, retval, name.c_str(), empty_bytebuf, - tid, pid, ppid, "", not_relevant_64, not_relevant_64, not_relevant_64, - not_relevant_32, not_relevant_32, not_relevant_32, name.c_str(), - cgroup_byte_buf, flags, not_relevant_32, not_relevant_32, vtid, vpid); - } - + const std::string& name = "bash", const std::vector& cgroup_vec = {}, + ppm_event_code event_type = PPME_SYSCALL_CLONE_20_X); sinsp_evt* generate_execve_enter_and_exit_event(int64_t retval, int64_t old_tid, int64_t new_tid, int64_t pid, - int64_t ppid, std::string pathname = "/bin/test-exe", - std::string comm = "test-exe", - std::string resolved_kernel_path = "/bin/test-exe", - std::vector cgroup_vec = {}) - { - /* Scaffolding needed to call the PPME_SYSCALL_EXECVE_19_X */ - uint64_t not_relevant_64 = 0; - uint32_t not_relevant_32 = 0; - scap_const_sized_buffer empty_bytebuf = {/*.buf =*/nullptr, /*.size =*/0}; - scap_const_sized_buffer cgroup_byte_buf = empty_bytebuf; - std::string cgroupsv = test_utils::to_null_delimited(cgroup_vec); - - /* If the cgroup vector is not empty overwrite it */ - if(!cgroup_vec.empty()) - { - cgroup_byte_buf = scap_const_sized_buffer{cgroupsv.data(), cgroupsv.size()}; - } - - add_event_advance_ts(increasing_ts(), old_tid, PPME_SYSCALL_EXECVE_19_E, 1, pathname.c_str()); - /* we have an `old_tid` and a `new_tid` because if a secondary thread calls the - * execve the thread leader will take control so the `tid` between enter and exit event - * will change - * */ - return add_event_advance_ts( - increasing_ts(), new_tid, PPME_SYSCALL_EXECVE_19_X, 28, retval, pathname.c_str(), empty_bytebuf, - new_tid, pid, ppid, "", not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_32, - not_relevant_32, not_relevant_32, comm.c_str(), cgroup_byte_buf, empty_bytebuf, not_relevant_32, - not_relevant_64, not_relevant_32, not_relevant_32, not_relevant_64, not_relevant_64, - not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_64, not_relevant_32, - resolved_kernel_path.c_str()); - } - - void remove_thread(int64_t tid_to_remove, int64_t reaper_tid) - { - generate_proc_exit_event(tid_to_remove, reaper_tid); - /* Generate a random event on init to trigger the removal after proc exit */ - generate_random_event(); - } - - sinsp_evt* generate_proc_exit_event(int64_t tid_to_remove, int64_t reaper_tid) - { - /* Scaffolding needed to call the PPME_PROCEXIT_1_E */ - int64_t not_relevant_64 = 0; - uint8_t not_relevant_8 = 0; - - return add_event_advance_ts(increasing_ts(), tid_to_remove, PPME_PROCEXIT_1_E, 5, not_relevant_64, not_relevant_64, not_relevant_8, not_relevant_8, reaper_tid); - } - - sinsp_evt* generate_random_event(int64_t tid_caller = INIT_TID) - { - /* Generate a random event on init to trigger the removal after proc exit */ - return add_event_advance_ts(increasing_ts(), tid_caller, PPME_SYSCALL_GETCWD_E, 0); - } - - /*=============================== PROCESS GENERATION ===========================*/ - - void add_thread(const scap_threadinfo &tinfo, const std::vector &fdinfos) - { - m_threads.push_back(tinfo); - m_test_data->threads = m_threads.data(); - m_test_data->thread_count = m_threads.size(); + int64_t ppid, const std::string& pathname = "/bin/test-exe", + const std::string& comm = "test-exe", + const std::string& resolved_kernel_path = "/bin/test-exe", + const std::vector& cgroup_vec = {}); + void remove_thread(int64_t tid_to_remove, int64_t reaper_tid); + sinsp_evt* generate_proc_exit_event(int64_t tid_to_remove, int64_t reaper_tid); + sinsp_evt* generate_random_event(int64_t tid_caller = INIT_TID); - m_fdinfos.push_back(fdinfos); - scap_test_fdinfo_data fdinfo_descriptor = { - /*.fdinfos =*/ m_fdinfos.back().data(), - /*.fdinfo_count =*/ m_fdinfos.back().size() - }; - m_test_fdinfo_data.push_back(fdinfo_descriptor); + //=============================== PROCESS GENERATION =========================== - m_test_data->fdinfo_data = m_test_fdinfo_data.data(); - } - - void set_threadinfo_last_access_time(int64_t tid, uint64_t access_time_ns) - { - auto tinfo = m_inspector.get_thread_ref(tid, false).get(); - if(tinfo != nullptr) - { - tinfo->m_lastaccess_ts = access_time_ns; - } - else - { - throw sinsp_exception("There is no thread info associated with tid: " + std::to_string(tid)); - } - } - - /* Remove all threads with `tinfo->m_lastaccess_ts` minor than `m_lastevent_ts - thread_timeout` */ - void remove_inactive_threads(uint64_t m_lastevent_ts, uint64_t thread_timeout) - { - /* We need to set these 2 variables to enable the remove_inactive_logic */ - m_inspector.m_thread_manager->set_last_flush_time_ns(1); - m_inspector.m_threads_purging_scan_time_ns = 2; - - m_inspector.set_lastevent_ts(m_lastevent_ts); - m_inspector.m_thread_timeout_ns = thread_timeout; - m_inspector.remove_inactive_threads(); - } + void add_thread(const scap_threadinfo&, const std::vector&); + void set_threadinfo_last_access_time(int64_t tid, uint64_t access_time_ns); + void remove_inactive_threads(uint64_t m_lastevent_ts, uint64_t thread_timeout); static scap_threadinfo create_threadinfo( uint64_t tid, uint64_t pid, uint64_t ptid, uint64_t vpgid, int64_t vtid, int64_t vpid, - std::string comm, std::string exe, std::string exepath, uint64_t clone_ts, uint32_t uid, uint32_t gid, - - std::vector args={}, uint64_t sid=0, std::vector env={}, std::string cwd="/test", - int64_t fdlimit=0x100000, uint32_t flags=0, bool exe_writable=true, - uint64_t cap_permitted=0x1ffffffffff, uint64_t cap_inheritable=0, uint64_t cap_effective=0x1ffffffffff, - uint32_t vmsize_kb=10000, uint32_t vmrss_kb=100, uint32_t vmswap_kb=0, uint64_t pfmajor=222, uint64_t pfminor=22, - std::vector cgroups={}, std::string root="/", int filtered_out=0, uint32_t tty=0, uint32_t loginuid=UINT32_MAX) - { - scap_threadinfo tinfo = {}; - tinfo.tid = tid; - tinfo.pid = pid; - tinfo.ptid = ptid; - tinfo.sid = sid; - tinfo.vpgid = vpgid; - tinfo.exe_writable = exe_writable; - tinfo.fdlimit = fdlimit; - tinfo.flags = flags; - tinfo.uid = uid; - tinfo.gid = gid; - tinfo.cap_permitted = cap_permitted; - tinfo.cap_effective = cap_effective; - tinfo.cap_inheritable = cap_inheritable; - tinfo.vmsize_kb = vmsize_kb; - tinfo.vmrss_kb = vmrss_kb; - tinfo.pfmajor = pfmajor; - tinfo.pfminor = pfminor; - tinfo.vtid = vtid; - tinfo.vpid = vpid; - tinfo.filtered_out = filtered_out; - tinfo.fdlist = nullptr; - tinfo.clone_ts = clone_ts; - tinfo.tty = tty; - tinfo.loginuid = loginuid; - - std::string argsv = ""; - if (!args.empty()) - { - argsv = test_utils::to_null_delimited(args); - argsv.push_back('\0'); - } - - std::string envv = ""; - if (!env.empty()) - { - envv = test_utils::to_null_delimited(env); - envv.push_back('\0'); - } - - std::string cgroupsv = ""; - if (!cgroups.empty()) - { - cgroupsv = test_utils::to_null_delimited(cgroups); - cgroupsv.push_back('\0'); - } - - memcpy(tinfo.args, argsv.data(), argsv.size()); - tinfo.args_len = argsv.size(); - memcpy(tinfo.env, envv.data(), envv.size()); - tinfo.env_len = envv.size(); - memcpy(tinfo.cgroups.path, cgroupsv.data(), cgroupsv.size()); - tinfo.cgroups.len = cgroupsv.size(); - - strlcpy(tinfo.cwd, cwd.c_str(), sizeof(tinfo.cwd)); - strlcpy(tinfo.comm, comm.c_str(), sizeof(tinfo.comm)); - strlcpy(tinfo.exe, exe.c_str(), sizeof(tinfo.exe)); - strlcpy(tinfo.exepath, exepath.c_str(), sizeof(tinfo.exepath)); - strlcpy(tinfo.root, root.c_str(), sizeof(tinfo.root)); - return tinfo; - } - - void add_default_init_thread() - { - std::vector env = {"TEST_ENV_PARENT_LINEAGE=secret", "HOME=/home/user/parent"}; - scap_threadinfo tinfo = create_threadinfo(1, 1, 0, 1, 1, 1, "init", "/sbin/init", "/sbin/init", increasing_ts(), 0, 0, {}, 0, env, "/root/"); - - std::vector fdinfos; - scap_fdinfo fdinfo; - fdinfo.fd = 0; - fdinfo.ino = 5; - fdinfo.type = SCAP_FD_FILE_V2; - - fdinfo.info.regularinfo.open_flags = PPM_O_RDONLY; - fdinfo.info.regularinfo.mount_id = 25; - fdinfo.info.regularinfo.dev = 0; - strlcpy(fdinfo.info.regularinfo.fname, "/dev/null", sizeof(fdinfo.info.regularinfo.fname)); - - fdinfos.push_back(fdinfo); - - add_thread(tinfo, fdinfos); - } - - void add_simple_thread(int64_t tid, int64_t pid, int64_t ptid, std::string comm = "random") - { - scap_threadinfo tinfo = create_threadinfo(tid, pid, ptid, tid, tid, pid, comm, "/sbin/init", "/sbin/init", increasing_ts(), 0, 0, {}, 0, {}, "/root/"); - add_thread(tinfo, {}); - } - - uint64_t increasing_ts() - { - uint64_t ret = m_test_timestamp; - m_test_timestamp += 10000000; // 10 msec increment - return ret; - } - - // Return true if `field_name` exists in the filtercheck list. - // The field value could also be NULL, but in this method, we are not interested in the value. - bool field_exists(sinsp_evt *evt, const std::string& field_name) - { - return field_exists(evt, field_name, m_default_filterlist); - } - - bool field_exists(sinsp_evt *evt, const std::string& field_name, filter_check_list& flist) - { - if (evt == nullptr) { - throw sinsp_exception("The event class is NULL"); - } - - auto new_fl = flist.new_filter_check_from_fldname(field_name, &m_inspector, false); - if(new_fl != nullptr) - { - // if we can create a filter check it means that the field exists - delete new_fl; - return true; - } - else - { - return false; - } - } - - // Return true if `field_name` value is not NULL for this event. - bool field_has_value(sinsp_evt *evt, const std::string& field_name) - { - return field_has_value(evt, field_name, m_default_filterlist); - } - - bool field_has_value(sinsp_evt *evt, const std::string& field_name, filter_check_list& flist) - { - if (evt == nullptr) { - throw sinsp_exception("The event class is NULL"); - } - - std::unique_ptr chk(flist.new_filter_check_from_fldname(field_name, &m_inspector, false)); - if(chk == nullptr) - { - throw sinsp_exception("The field " + field_name + " is not a valid field."); - } - /* we created a filter check starting from the field name so if we arrive here we will find it for sure */ - chk->parse_field_name(field_name.c_str(), true, false); - std::vector values; - return chk->extract(evt, values); - } - - std::string get_field_as_string(sinsp_evt *evt, const std::string& field_name) - { - return get_field_as_string(evt, field_name, m_default_filterlist); - } - - std::string get_field_as_string(sinsp_evt *evt, const std::string& field_name, filter_check_list& flist) - { - if (evt == nullptr) { - throw sinsp_exception("The event class is NULL"); - } - - std::unique_ptr chk(flist.new_filter_check_from_fldname(field_name, &m_inspector, false)); - if(chk == nullptr) - { - throw sinsp_exception("The field " + field_name + " is not a valid field."); - } - /* we created a filter check starting from the field name so if we arrive here we will find it for sure */ - chk->parse_field_name(field_name.c_str(), true, false); - - const char* result = chk->tostring(evt); - if (result == nullptr) { - throw sinsp_exception("The field " + field_name + " is NULL"); - } - - return result; - } - - sinsp_evt *next_event() - { - sinsp_evt *evt; - auto result = m_inspector.next(&evt); - return result == SCAP_SUCCESS ? evt : nullptr; - } - - std::unique_ptr m_test_data; + const std::string& comm, const std::string& exe, const std::string& exepath, + uint64_t clone_ts, uint32_t uid, uint32_t gid, + const std::vector& args, uint64_t sid, const std::vector& env, + const std::string& cwd, + int64_t fdlimit = 0x100000, uint32_t flags = 0, bool exe_writable = true, + uint64_t cap_permitted = 0x1ffffffffff, uint64_t cap_inheritable = 0, uint64_t cap_effective = 0x1ffffffffff, + uint32_t vmsize_kb = 10000, uint32_t vmrss_kb = 100, uint32_t vmswap_kb = 0, uint64_t pfmajor = 222, uint64_t pfminor = 22, + const std::vector& cgroups = {}, const std::string& root = "/", + int filtered_out = 0, uint32_t tty = 0, uint32_t loginuid = UINT32_MAX); + + void add_default_init_thread(); + void add_simple_thread(int64_t tid, int64_t pid, int64_t ptid, const std::string& comm = "random"); + uint64_t increasing_ts(); + bool field_exists(sinsp_evt*, const std::string& field_name); + bool field_exists(sinsp_evt*, const std::string& field_name, filter_check_list&); + bool field_has_value(sinsp_evt*, const std::string& field_name); + bool field_has_value(sinsp_evt*, const std::string& field_name, filter_check_list&); + std::string get_field_as_string(sinsp_evt*, const std::string& field_name); + std::string get_field_as_string(sinsp_evt*, const std::string& field_name, filter_check_list&); + sinsp_evt* next_event(); + + scap_test_input_data m_test_data; std::vector m_events; std::vector m_async_events; @@ -527,6 +106,6 @@ class sinsp_with_test_input : public ::testing::Test { std::vector m_test_fdinfo_data; sinsp_filter_check_list m_default_filterlist; - uint64_t m_test_timestamp; - uint64_t m_last_recorded_timestamp; + uint64_t m_test_timestamp = 1566230400000000000; + uint64_t m_last_recorded_timestamp = 0; };