diff --git a/package/scripts/precooked_tests/drop/1/proc/1729/fd/0 b/package/scripts/precooked_tests/drop/1/proc/1729/fd/0 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/1/proc/1729/fd/1 b/package/scripts/precooked_tests/drop/1/proc/1729/fd/1 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/1/proc/1729/fd/2 b/package/scripts/precooked_tests/drop/1/proc/1729/fd/2 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/2/proc/1729/fd/0 b/package/scripts/precooked_tests/drop/2/proc/1729/fd/0 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/2/proc/1729/fd/1 b/package/scripts/precooked_tests/drop/2/proc/1729/fd/1 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/2/proc/1729/fd/2 b/package/scripts/precooked_tests/drop/2/proc/1729/fd/2 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/2/proc/1729/fd/3 b/package/scripts/precooked_tests/drop/2/proc/1729/fd/3 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/2/proc/1729/fd/4 b/package/scripts/precooked_tests/drop/2/proc/1729/fd/4 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/3/proc/1729/fd/0 b/package/scripts/precooked_tests/drop/3/proc/1729/fd/0 new file mode 100644 index 00000000..e69de29b diff --git a/package/scripts/precooked_tests/drop/3/proc/1729/fd/1 b/package/scripts/precooked_tests/drop/3/proc/1729/fd/1 new file mode 100644 index 00000000..e69de29b diff --git a/package/src/countmon.cpp b/package/src/countmon.cpp index 599144be..edd8c698 100644 --- a/package/src/countmon.cpp +++ b/package/src/countmon.cpp @@ -3,6 +3,7 @@ #include "countmon.h" +#include #include #include @@ -49,6 +50,19 @@ void countmon::update_stats(const std::vector& pids, std::stol(stat_entries[prmon::num_threads]); } stat_entries.clear(); + + std::stringstream fd_dir_name{}; + fd_dir_name << read_path << "/proc/" << pid << "/fd" << std::ends; + DIR* fd_dir = opendir(fd_dir_name.str().c_str()); + if (fd_dir) { + struct dirent* entry; + while ((entry = readdir(fd_dir)) != nullptr) { + if (entry->d_name[0] != '.') { + count_stat_update["nfds"] += 1L; + } + } + closedir(fd_dir); + } } // Update the statistics with the new snapshot values diff --git a/package/src/countmon.h b/package/src/countmon.h index 709b5bfb..bd288a7e 100644 --- a/package/src/countmon.h +++ b/package/src/countmon.h @@ -1,7 +1,7 @@ // Copyright (C) 2018-2025 CERN // License Apache2 - see LICENCE file -// Process and thread number monitoring class +// Process, thread and file descriptor number monitoring class // #ifndef PRMON_COUNTMON_H @@ -21,7 +21,8 @@ class countmon final : public Imonitor, public MessageBase { private: // Setup the parameters to monitor here const prmon::parameter_list params = {{"nprocs", "1", "1"}, - {"nthreads", "1", "1"}}; + {"nthreads", "1", "1"}, + {"nfds", "1", "1"}}; // Dynamic monitoring container for value measurements // This will be filled at initialisation, taking the names @@ -46,6 +47,7 @@ class countmon final : public Imonitor, public MessageBase { void const get_unit_info(nlohmann::json& unit_json); bool const is_valid() { return true; } }; -REGISTER_MONITOR(Imonitor, countmon, "Monitor number of processes and threads") +REGISTER_MONITOR(Imonitor, countmon, + "Monitor number of processes, threads and file descriptors") #endif // PRMON_COUNTMON_H diff --git a/package/tests/test_count.py b/package/tests/test_count.py index 46dcafe0..290985d9 100755 --- a/package/tests/test_count.py +++ b/package/tests/test_count.py @@ -65,6 +65,17 @@ def test_run_test_with_params(self): f"(expected {expect_thread}, got {total_thread}", ) + # File descriptor count sanity check: every process + # has at least stdin, stdout, stderr open + total_nfds = prmon_json["Max"]["nfds"] + min_expected_nfds = 3 * procs + self.assertGreaterEqual( + total_nfds, + min_expected_nfds, + msg="File descriptor count too low " + f"(expected >= {min_expected_nfds}, got {total_nfds})", + ) + return ConfigurableProcessMonitor diff --git a/package/tests/test_values.cpp b/package/tests/test_values.cpp index bb1a151b..c325ac43 100644 --- a/package/tests/test_values.cpp +++ b/package/tests/test_values.cpp @@ -28,6 +28,37 @@ std::shared_ptr f_sink{ bool prmon::sigusr1 = false; +TEST(CountmonTest, CountmonValueTestFixed) { + std::string cur_path = base_path + "drop"; + std::vector fake_pids = mother_pid; + + std::unique_ptr monitor( + registry::Registry::create("countmon")); + + const int iterationCount = 3; + + std::map store_stats; + for (int iteration = 1; iteration <= iterationCount; ++iteration) { + std::stringstream iteration_path{}; + iteration_path << cur_path << "/" << iteration; + monitor->update_stats(fake_pids, iteration_path.str()); + store_stats = monitor->get_text_stats(); + ASSERT_EQ(store_stats["nprocs"], 1); + ASSERT_EQ(store_stats["nthreads"], 1); + if (iteration == 1) { + ASSERT_EQ(store_stats["nfds"], 3); + } else if (iteration == 2) { + ASSERT_EQ(store_stats["nfds"], 5); + } else { + ASSERT_EQ(store_stats["nfds"], 2); + } + } + + // Verify that the peak value reflects the maximum across iterations + auto max_stats = monitor->get_json_total_stats(); + ASSERT_EQ(max_stats["nfds"], 5); +} + TEST(IomonTest, IomonMonitonicityTestFixed) { std::string cur_path = base_path + "drop"; std::vector fake_pids = mother_pid;