From 4c589b5c16442df6668dcb6bd90a811f0513bdc3 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Tue, 5 Aug 2025 13:41:21 +0200 Subject: [PATCH 1/7] Switched syscall_intercept repository --- src/posix/syscall_intercept/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/posix/syscall_intercept/CMakeLists.txt b/src/posix/syscall_intercept/CMakeLists.txt index 517f76217..e769f6dc2 100644 --- a/src/posix/syscall_intercept/CMakeLists.txt +++ b/src/posix/syscall_intercept/CMakeLists.txt @@ -14,12 +14,13 @@ include(ExternalProject) # Import external project from git ##################################### ExternalProject_Add(syscall_intercept - GIT_REPOSITORY https://github.com/pmem/syscall_intercept.git - GIT_TAG ca4b13531f883597c2f04a40e095f76f6c3a6d22 + GIT_REPOSITORY https://github.com/alpha-unito/syscall_intercept.git + GIT_TAG b05ff8037de2eb7b44dfb7fa372cfe08d565ba84 PREFIX ${CMAKE_CURRENT_BINARY_DIR} CMAKE_ARGS + -DSTATIC_CAPSTONE=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -) \ No newline at end of file +) From a517294de5925edd32d1b5b1607c397dd5c79dd6 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 28 Aug 2025 11:18:28 +0200 Subject: [PATCH 2/7] fix --- .github/workflows/ci-tests.yaml | 10 ++++++---- src/posix/syscall_intercept/CMakeLists.txt | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index 1a67a994c..2e63929b2 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -157,10 +157,12 @@ jobs: export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}" echo "Run CAPIO syscall Unit tests" - LD_PRELOAD=libcapio_posix.so \ - capio_syscall_unit_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 + #LD_PRELOAD=libcapio_posix.so \ + #capio_syscall_unit_tests \ + # --gtest_break_on_failure \ + # --gtest_print_time=1 + + capiorun -d $CAPIO_DIR -n default_app -L -1 capio_syscall_unit_tests --gtest_break_on_failure --gtest_print_time=1 echo "Run CAPIO SERVER unit tests" capio_server_unit_tests \ diff --git a/src/posix/syscall_intercept/CMakeLists.txt b/src/posix/syscall_intercept/CMakeLists.txt index e769f6dc2..df762ade0 100644 --- a/src/posix/syscall_intercept/CMakeLists.txt +++ b/src/posix/syscall_intercept/CMakeLists.txt @@ -18,7 +18,7 @@ ExternalProject_Add(syscall_intercept GIT_TAG b05ff8037de2eb7b44dfb7fa372cfe08d565ba84 PREFIX ${CMAKE_CURRENT_BINARY_DIR} CMAKE_ARGS - -DSTATIC_CAPSTONE=ON + -DSTATIC_CAPSTONE=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} From cda3a95f0571bd0c78c1f59d0e4ceddd84f443f8 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 28 Aug 2025 11:40:30 +0200 Subject: [PATCH 3/7] fixes --- .github/workflows/ci-tests.yaml | 31 +-- capiorun/capiorun | 321 ++++++++++++++++------------ capiorun/server.err | 0 server.err | 0 tests/unit/MemoryFiles/src/main.cpp | 82 ------- tests/unit/syscall/src/main.cpp | 79 ------- 6 files changed, 206 insertions(+), 307 deletions(-) create mode 100644 capiorun/server.err create mode 100644 server.err diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index 2e63929b2..170956fb0 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -156,30 +156,35 @@ jobs: run: | export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}" - echo "Run CAPIO syscall Unit tests" - #LD_PRELOAD=libcapio_posix.so \ - #capio_syscall_unit_tests \ - # --gtest_break_on_failure \ - # --gtest_print_time=1 + echo "Run CAPIO syscall Unit tests" + capio_server --no-config & + CAPIO_SERVER_PID=$! + LD_PRELOAD=libcapio_posix.so capio_syscall_unit_tests \ + --gtest_break_on_failure --gtest_print_time=1 + kill $CAPIO_SERVER_PID + + + echo "Run CAPIO memory file integration tests" + capio_server --no-config & + CAPIO_SERVER_PID=$! + LD_PRELOAD=libcapio_posix.so capio_memory_file_unit_tests \ + --gtest_break_on_failure --gtest_print_time=1 + kill $CAPIO_SERVER_PID - capiorun -d $CAPIO_DIR -n default_app -L -1 capio_syscall_unit_tests --gtest_break_on_failure --gtest_print_time=1 - echo "Run CAPIO SERVER unit tests" - capio_server_unit_tests \ - --gtest_break_on_failure \ - --gtest_print_time=1 echo "Run CAPIO POSIX Unit tests" capio_posix_unit_tests \ --gtest_break_on_failure \ --gtest_print_time=1 - echo "Run CAPIO memory file integration tests" - LD_PRELOAD=libcapio_posix.so \ - capio_memory_file_unit_tests \ + + echo "Run CAPIO SERVER unit tests" + capio_server_unit_tests \ --gtest_break_on_failure \ --gtest_print_time=1 + - name: "Show client logs on failure" if: ${{ always() && steps.run-tests.outcome == 'failure' && matrix.build_type == 'Debug' }} run: tail -v -n +1 capio_logs/posix/$(hostname)/posix_thread_*.log diff --git a/capiorun/capiorun b/capiorun/capiorun index e1f071168..5e236c872 100755 --- a/capiorun/capiorun +++ b/capiorun/capiorun @@ -4,101 +4,54 @@ import argparse import json import os import signal +import socket import subprocess import sys import time -parser = argparse.ArgumentParser( - prog="capiorun", - description=""" -capiorun - Simplified launcher for CAPIO-based applications. - -This utility streamlines the setup and execution of CAPIO workflows by automatically configuring -the environment and managing capio_server instances. It allows running specific application steps -defined in a CAPIO-CL configuration without manual setup of environment variables. - -Typical usage: - capiorun -d /mnt/capio -n myapp -c config.json -- -""", - epilog=""" -Developed by Marco Edoardo Santimaria - -For more information, refer to the CAPIO documentation or repository. -""", - formatter_class=argparse.RawTextHelpFormatter, -) - -# Required arguments -parser.add_argument( - "-d", - "--capio-dir", - required=True, - help="CAPIO virtual mount point (e.g., /mnt/capio)", -) -parser.add_argument( - "-n", - "--app-name", - required=True, - help="Name of the CAPIO application step to launch. Must match an entry in the CAPIO-CL config.", -) - -# Optional but commonly used -parser.add_argument( - "-c", - "--capiocl", - default="--no-config", - help="Path to the CAPIO-CL configuration file (default: --no-config)", -) -parser.add_argument( - "-m", "--metadata-dir", default="", help="Custom directory for metadata" -) - - -# Debug and logging -parser.add_argument( - "-L", - "--log-level", - default="-1", - help="CAPIO log level. Useful when running in debug mode (default: -1)", -) -parser.add_argument( - "--log-dir", default="", help="Custom directory for CAPIO log output" -) -parser.add_argument("--log-prefix", default="", help="Prefix for CAPIO log files") - -# Tuning and advanced -parser.add_argument( - "--cache-lines", - default="", - help="Number of CAPIO shm-queue cache lines (optional tuning parameter)", -) -parser.add_argument( - "--init-file-size", - default="", - help="Default file size (in bytes) when pre-allocating memory for new files", -) - -# Binary locations -parser.add_argument( - "-l", - "--libcapio", - default="libcapio_posix.so", - help="Path to libcapio_posix.so shared library (default: libcapio_posix.so)", -) -parser.add_argument( - "-s", - "--server", - default="capio_server", - help="Path to capio_server executable (default: capio_server)", -) - -# Positional arguments -parser.add_argument( - "args", nargs=argparse.REMAINDER, help="Command to launch with capio" -) - - -def build_env(args, workflow_name): + +################### +### Single Node ### +################### + +### Single App Setup ### +# Location: node1 +# Processes: capiorun1, server, app1 +# Events: +# - app1 completes execution +# - capiorun1 sends SIGUSR1 to the server +# - Server can terminate after handling the signal + +### Multi-App Setup ### +# Location: node1 +# Processes: capiorun1, capiorun2, server, app1, app2 +# Events: +# - app1 completes execution +# - capiorun1 does NOT send SIGUSR1 to the server if app2 is still running +# - app2 completes execution +# - capiorun2 sends SIGUSR1 to the server, signaling completion + + +################### +### Multi-Node ### +################### + +### Single App on Each Node ### +# Node 1 Node 2 +# Processes: capiorun1 capiorun2 +# server1 server2 +# app1 app2 +# Events: +# - app1 finishes execution +# - app2 consumes app1's output +# - server1 (Capio server) processes app2's request +# - capiorun1 sends SIGUSR1 to server1 +# - server1 completes the request and terminates +# - app2 finishes execution +# - capiorun2 sends SIGUSR1 to server2, signaling completion + + +def build_env(args, workflow_name, is_server=False): env = os.environ.copy() if args.log_dir: env["CAPIO_LOG_DIR"] = args.log_dir @@ -108,12 +61,14 @@ def build_env(args, workflow_name): env["CAPIO_CACHE_LINES"] = args.cache_lines if args.init_file_size: env["CAPIO_FILE_INIT_SIZE"] = args.init_file_size - env["CAPIO_DIR"] = args.capio_dir env["CAPIO_LOG_LEVEL"] = args.log_level env["CAPIO_WORKFLOW_NAME"] = workflow_name - env["CAPIO_METADATA_DIR"] = args.metadata_dir - + if is_server: + env["CAPIO_METADATA_DIR"] = args.metadata_dir + else: # is a client + env["CAPIO_APP_NAME"] = args.app_name + env["LD_PRELOAD"] = args.libcapio return env @@ -126,62 +81,64 @@ def count_files_starting_with(prefix): ) -server_process = None -step_process = None +def main(args): + server_process = None + step_process = None -if __name__ == "__main__": - args = parser.parse_args() - - workflow_name = "CAPIO" if args.capiocl != "--no-config": with open(args.capiocl, "r") as f: workflow_name = json.load(f)["name"] + else: + workflow_name = "CAPIO" if not os.path.exists(f"/dev/shm/{workflow_name}"): print(f"Starting capio server with config file: {args.capiocl}") - print(f"CAPIO_LOG_LEVEL = {args.log_level}") - print(f"CAPIO_WORKFLOW_NAME = {workflow_name}") print(f"CAPIO_APP_NAME = {args.app_name}") print(f"CAPIO_DIR = {args.capio_dir}") - print(f"CAPIO-CL CONFIG = {args.capiocl}") + print(f"CAPIO_CL_CONFIG = {args.capiocl}") + print(f"CAPIO_LOG_DIR = {args.log_dir}") + print(f"CAPIO_LOG_LEVEL = {args.log_level}") + print(f"CAPIO_METADATA_DIR = {args.metadata_dir}") + print(f"CAPIO_WORKFLOW_NAME = {workflow_name}") if not os.path.exists(args.capiocl) and args.capiocl != "--no-config": print(f"File {args.capiocl} does not exists. aborting execution...") exit(1) - server_env = build_env(args, workflow_name) - - server_process = subprocess.Popen( - [ - args.server, - ( - ("--config " + args.capiocl) - if args.capiocl != "--no-config" - else args.capiocl - ), - ], - env=server_env, - stdout="server.log", - stderr="server.err", - cwd=args.log_dir, - start_new_session=True, - ) + server_env = build_env(args, workflow_name, is_server=True) - print(f"capio_server PID: {server_process.pid}") - time.sleep(1) + hostname = socket.gethostname() + pid = os.getpid() + with open( + os.path.join(args.log_dir, f"server_{hostname}_{pid}.out"), "w" + ) as out_file: + server_process = subprocess.Popen( + [ + args.server, + ( + "--config " + args.capiocl + if args.capiocl != "--no-config" + else args.capiocl + ), + ], + env=server_env, + cwd=args.log_dir, + #stdout=out_file, + #stderr=out_file, + start_new_session=True, + ) + print(f"{hostname} capiorun-pid {pid} - capio_server PID: {server_process.pid}") + time.sleep(1) else: print( f"An instance of capio_server with workflow name {workflow_name} already exists!" ) - step_env = build_env(args, workflow_name) - step_env["CAPIO_APP_NAME"] = args.app_name - step_env["LD_PRELOAD"] = args.libcapio print(f"Starting workflow steps with following environment variables:") - print(f"CAPIO_LOG_LEVEL = {args.log_level}") - print(f"CAPIO_WORKFLOW_NAME = {workflow_name}") print(f"CAPIO_APP_NAME = {args.app_name}") print(f"CAPIO_DIR = {args.capio_dir}") + print(f"CAPIO_LOG_LEVEL = {args.log_level}") + print(f"CAPIO_WORKFLOW_NAME = {workflow_name}") print(f"LD_PRELOAD = {args.libcapio}") print(f"command = {' '.join(args.args)}") try: @@ -191,31 +148,34 @@ if __name__ == "__main__": stdout=sys.stdout, stderr=sys.stderr, ) - step_process.wait() - print(f"Step {args.app_name} terminated successfully") + print( + f"Step {args.app_name} terminated with returncode:", step_process.returncode + ) except Exception as e: print( f"An error occurred in startup/execution of workflow app <{args.app_name}>: {e}" ) if server_process is not None: - if count_files_starting_with(workflow_name) >= 4: + if count_files_starting_with(workflow_name) > 4: print( "Server instance is used by other applications... skipping server termination" ) else: print(f"Terminating instance of capio_server") - server_process.send_signal(signal.SIGTERM) + server_process.send_signal(signal.SIGUSR1) time.sleep(2) print("Terminated CAPIO server instance") else: - if count_files_starting_with(workflow_name) < 4: + if count_files_starting_with(workflow_name) <= 4: print( "Terminating instance of capio_server started by other capiorun command" ) result = subprocess.run( - ["killall", "capio_server"], stdout=sys.stdout, stderr=sys.stderr + ["killall", "-USR1", "capio_server"], + stdout=sys.stdout, + stderr=sys.stderr, ) if result.returncode == 0: print("Terminated CAPIO server instance") @@ -223,3 +183,98 @@ if __name__ == "__main__": print("Error terminating capio_server instance!") else: print("Skipping termination of capio_server") + + +def parse_args(): + parser = argparse.ArgumentParser( + prog="capiorun", + description=""" + capiorun - Simplified launcher for CAPIO-based applications. + + This utility streamlines the setup and execution of CAPIO workflows by automatically configuring + the environment and managing capio_server instances. It allows running specific application steps + defined in a CAPIO-CL configuration without manual setup of environment variables. + + Typical usage: + capiorun -d /mnt/capio -n myapp -c config.json -- + """, + epilog=""" + Developed by Marco Edoardo Santimaria + + For more information, refer to the CAPIO documentation or repository. + """, + formatter_class=argparse.RawTextHelpFormatter, + ) + + # Required arguments + parser.add_argument( + "-d", + "--capio-dir", + required=True, + help="CAPIO virtual mount point (e.g., /mnt/capio)", + ) + parser.add_argument( + "-n", + "--app-name", + required=True, + help="Name of the CAPIO application step to launch. Must match an entry in the CAPIO-CL config.", + ) + + # Optional but commonly used + parser.add_argument( + "-c", + "--capiocl", + default="--no-config", + help="Path to the CAPIO-CL configuration file (default: --no-config)", + ) + parser.add_argument( + "-m", "--metadata-dir", default="", help="Custom directory for metadata" + ) + + # Debug and logging + parser.add_argument( + "-L", + "--log-level", + default="-1", + help="CAPIO log level. Useful when running in debug mode (default: -1)", + ) + parser.add_argument( + "--log-dir", default="", help="Custom directory for CAPIO log output" + ) + parser.add_argument("--log-prefix", default="", help="Prefix for CAPIO log files") + + # Tuning and advanced + parser.add_argument( + "--cache-lines", + default="", + help="Number of CAPIO shm-queue cache lines (optional tuning parameter)", + ) + parser.add_argument( + "--init-file-size", + default="", + help="Default file size (in bytes) when pre-allocating memory for new files", + ) + + # Binary locations + parser.add_argument( + "-l", + "--libcapio", + default="libcapio_posix.so", + help="Path to libcapio_posix.so shared library (default: libcapio_posix.so)", + ) + parser.add_argument( + "-s", + "--server", + default="capio_server", + help="Path to capio_server executable (default: capio_server)", + ) + + # Positional arguments + parser.add_argument( + "args", nargs=argparse.REMAINDER, help="Command to launch with capio" + ) + return parser.parse_args() + + +if __name__ == "__main__": + main(parse_args()) diff --git a/capiorun/server.err b/capiorun/server.err new file mode 100644 index 000000000..e69de29bb diff --git a/server.err b/server.err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/MemoryFiles/src/main.cpp b/tests/unit/MemoryFiles/src/main.cpp index e37a74275..77b800299 100644 --- a/tests/unit/MemoryFiles/src/main.cpp +++ b/tests/unit/MemoryFiles/src/main.cpp @@ -9,87 +9,6 @@ #include #include -char **build_args() { - char **args = (char **) malloc(4 * sizeof(uintptr_t)); - - char const *command = std::getenv("CAPIO_SERVER_PATH"); - if (command == nullptr) { - command = "capio_server"; - } - - args[0] = strdup(command); - args[1] = strdup("--no-config"); - args[2] = strdup("--mem-only"); - args[3] = (char *) nullptr; - - return args; -} - -char **build_env(char **envp) { - std::vector vars; - for (int i = 0; envp[i] != nullptr; i++) { - const std::string_view var(envp[i]); - const std::string_view key = var.substr(0, var.find('=')); - if (key != "LD_PRELOAD") { - vars.emplace_back(i); - } - } - - char **cleaned_env = (char **) malloc((vars.size() + 2) * sizeof(uintptr_t)); - for (size_t i = 0; i < vars.size(); i++) { - cleaned_env[i] = strdup(envp[i]); - } - - cleaned_env[vars.size()] = strdup("LD_PRELOAD="); - cleaned_env[vars.size() + 1] = (char *) nullptr; - - return cleaned_env; -} - -class CapioServerEnvironment : public testing::Environment { - private: - char **args; - char **envp; - int server_pid; - - public: - explicit CapioServerEnvironment(char **envp) - : args(build_args()), envp(build_env(envp)), server_pid(-1) {}; - - ~CapioServerEnvironment() override { - for (int i = 0; args[i] != nullptr; i++) { - free(args[i]); - } - free(args); - for (int i = 0; envp[i] != nullptr; i++) { - free(envp[i]); - } - free(envp); - }; - - void SetUp() override { - if (server_pid < 0) { - ASSERT_NE(std::getenv("CAPIO_DIR"), nullptr); - ASSERT_GE(server_pid = fork(), 0); - if (server_pid == 0) { - execvpe(args[0], args, envp); - std::cout << "Error: unable to start server" << std::endl; - _exit(127); - } else { - sleep(5); - } - } - } - - void TearDown() override { - if (server_pid > 0) { - kill(server_pid, SIGTERM); - waitpid(server_pid, nullptr, 0); - server_pid = -1; - } - } -}; - const std::string filename = "hello.txt"; constexpr size_t textSize = 32 * 1024 * 1024; // 32 MBB @@ -131,7 +50,6 @@ TEST(CapioMemoryFileTest, TestReadAndWrite32MBFile) { int main(int argc, char **argv, char **envp) { testing::InitGoogleTest(&argc, argv); - testing::AddGlobalTestEnvironment(new CapioServerEnvironment(envp)); return RUN_ALL_TESTS(); } \ No newline at end of file diff --git a/tests/unit/syscall/src/main.cpp b/tests/unit/syscall/src/main.cpp index 338822761..be918012d 100644 --- a/tests/unit/syscall/src/main.cpp +++ b/tests/unit/syscall/src/main.cpp @@ -3,88 +3,9 @@ std::string workflow_name = CAPIO_DEFAULT_WORKFLOW_NAME; -char **build_args() { - char **args = (char **) malloc(3 * sizeof(uintptr_t)); - - char const *command = std::getenv("CAPIO_SERVER_PATH"); - if (command == nullptr) { - command = "capio_server"; - } - - args[0] = strdup(command); - args[1] = strdup("--no-config"); - args[2] = (char *) nullptr; - - return args; -} - -char **build_env(char **envp) { - std::vector vars; - for (int i = 0; envp[i] != nullptr; i++) { - const std::string_view var(envp[i]); - const std::string_view key = var.substr(0, var.find('=')); - if (key != "LD_PRELOAD") { - vars.emplace_back(i); - } - } - - char **cleaned_env = (char **) malloc((vars.size() + 2) * sizeof(uintptr_t)); - for (size_t i = 0; i < vars.size(); i++) { - cleaned_env[i] = strdup(envp[i]); - } - cleaned_env[vars.size()] = strdup("LD_PRELOAD="); - cleaned_env[vars.size() + 1] = (char *) nullptr; - - return cleaned_env; -} - -class CapioServerEnvironment : public testing::Environment { - private: - char **args; - char **envp; - int server_pid; - - public: - explicit CapioServerEnvironment(char **envp) - : args(build_args()), envp(build_env(envp)), server_pid(-1) {}; - - ~CapioServerEnvironment() override { - for (int i = 0; args[i] != nullptr; i++) { - free(args[i]); - } - free(args); - for (int i = 0; envp[i] != nullptr; i++) { - free(envp[i]); - } - free(envp); - }; - - void SetUp() override { - if (server_pid < 0) { - ASSERT_NE(std::getenv("CAPIO_DIR"), nullptr); - ASSERT_GE(server_pid = fork(), 0); - if (server_pid == 0) { - execvpe(args[0], args, envp); - std::cout << "Error: unable to start server" << std::endl; - _exit(127); - } else { - sleep(5); - } - } - } - - void TearDown() override { - if (server_pid > 0) { - kill(server_pid, SIGTERM); - waitpid(server_pid, nullptr, 0); - server_pid = -1; - } - } -}; int main(int argc, char **argv, char **envp) { testing::InitGoogleTest(&argc, argv); - testing::AddGlobalTestEnvironment(new CapioServerEnvironment(envp)); return RUN_ALL_TESTS(); } From e7d5ab3a1d4af27df53b78c07dbb66f068280575 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 28 Aug 2025 13:12:57 +0200 Subject: [PATCH 4/7] fix --- .github/workflows/ci-tests.yaml | 4 +++- src/posix/utils/requests.hpp | 12 +----------- src/server/client-manager/handlers/handshake.hpp | 15 ++++----------- tests/unit/syscall/src/main.cpp | 3 +-- 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index 170956fb0..ac4009a39 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -162,6 +162,7 @@ jobs: LD_PRELOAD=libcapio_posix.so capio_syscall_unit_tests \ --gtest_break_on_failure --gtest_print_time=1 kill $CAPIO_SERVER_PID + sleep 2 echo "Run CAPIO memory file integration tests" @@ -170,6 +171,7 @@ jobs: LD_PRELOAD=libcapio_posix.so capio_memory_file_unit_tests \ --gtest_break_on_failure --gtest_print_time=1 kill $CAPIO_SERVER_PID + sleep 2 @@ -177,6 +179,7 @@ jobs: capio_posix_unit_tests \ --gtest_break_on_failure \ --gtest_print_time=1 + sleep 2 echo "Run CAPIO SERVER unit tests" @@ -184,7 +187,6 @@ jobs: --gtest_break_on_failure \ --gtest_print_time=1 - - name: "Show client logs on failure" if: ${{ always() && steps.run-tests.outcome == 'failure' && matrix.build_type == 'Debug' }} run: tail -v -n +1 capio_logs/posix/$(hostname)/posix_thread_*.log diff --git a/src/posix/utils/requests.hpp b/src/posix/utils/requests.hpp index a7041d539..b47f248f4 100644 --- a/src/posix/utils/requests.hpp +++ b/src/posix/utils/requests.hpp @@ -51,20 +51,10 @@ inline void handshake_request(const long tid, const long pid, const std::string sprintf(req, "%04d %ld %ld %s", CAPIO_REQUEST_HANDSHAKE, tid, pid, app_name.c_str()); buf_requests->write(req, CAPIO_REQ_MAX_SIZE); -#ifndef CAPIO_BUILD_TESTS LOG("Waiting for response from capio_server"); - /* - * The handshake request must be blocking ONLY when not building tests. This is because when - * starting unit tests, the binary is loaded with libcapio_posix.so underneath thus performing - * a handshake request. If the handshake is blocking, then the capio_server binary cannot be - * started as the whole process is waiting for a handshake. - */ if (bufs_response->at(pid)->read() == 0) { ERR_EXIT("Error: handshake request sent while capio_server is shutting down!"); } -#endif - - LOG("Sent handshake request"); } /** @@ -170,4 +160,4 @@ inline void rename_request(const std::filesystem::path &old_path, #include "utils/storage.hpp" -#endif // CAPIO_POSIX_UTILS_REQUESTS_HPP +#endif // CAPIO_POSIX_UTILS_REQUESTS_HPP \ No newline at end of file diff --git a/src/server/client-manager/handlers/handshake.hpp b/src/server/client-manager/handlers/handshake.hpp index 3c01d8bb5..37d0cdb7b 100644 --- a/src/server/client-manager/handlers/handshake.hpp +++ b/src/server/client-manager/handlers/handshake.hpp @@ -22,25 +22,18 @@ inline void handshake_handler(const char *const str) { storage_service->register_client(app_name, tid); server_println(CAPIO_LOG_SERVER_CLI_LEVEL_INFO, "Registered new app: " + std::string(app_name)); - /* - * The handshake request must be blocking ONLY when not building tests. This is because when - * starting unit tests, the binary is loaded with libcapio_posix.so underneath thus - * performing a handshake request. If the handshake is blocking, then the capio_server - * binary cannot be started as the whole process is waiting for a handshake. - */ -#ifndef CAPIO_BUILD_TESTS + // Unlock client waiting to start LOG("Allowing handshake to continue"); client_manager->reply_to_client(tid, 1); -#endif + } else { -#ifndef CAPIO_BUILD_TESTS LOG("Termination phase is in progress. ignoring further handshakes."); client_manager->reply_to_client(tid, 0); server_println(CAPIO_LOG_SERVER_CLI_LEVEL_ERROR, "Termination phase is in progress. ignoring further handshakes."); -#endif + } } -#endif // HANDSHAKE_HPP +#endif // HANDSHAKE_HPP \ No newline at end of file diff --git a/tests/unit/syscall/src/main.cpp b/tests/unit/syscall/src/main.cpp index be918012d..f1710743f 100644 --- a/tests/unit/syscall/src/main.cpp +++ b/tests/unit/syscall/src/main.cpp @@ -3,9 +3,8 @@ std::string workflow_name = CAPIO_DEFAULT_WORKFLOW_NAME; - int main(int argc, char **argv, char **envp) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} +} \ No newline at end of file From 80175187b66a7683f4229118ce7f51323329fb15 Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 28 Aug 2025 13:19:04 +0200 Subject: [PATCH 5/7] fix --- src/server/client-manager/handlers/handshake.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/client-manager/handlers/handshake.hpp b/src/server/client-manager/handlers/handshake.hpp index 37d0cdb7b..9ba1f813e 100644 --- a/src/server/client-manager/handlers/handshake.hpp +++ b/src/server/client-manager/handlers/handshake.hpp @@ -31,7 +31,8 @@ inline void handshake_handler(const char *const str) { LOG("Termination phase is in progress. ignoring further handshakes."); client_manager->reply_to_client(tid, 0); server_println(CAPIO_LOG_SERVER_CLI_LEVEL_ERROR, - "Termination phase is in progress. ignoring further handshakes."); + "Termination phase is in progress. " + "ignoring further handshakes."); } } From 1f990655e19b62cc5ec4736c329dc566cb284fad Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 28 Aug 2025 13:39:33 +0200 Subject: [PATCH 6/7] fix --- capiorun/capiorun | 320 ++++++++---------- capiorun/server.err | 0 server.err | 0 .../client-manager/handlers/handshake.hpp | 4 +- 4 files changed, 133 insertions(+), 191 deletions(-) delete mode 100644 capiorun/server.err delete mode 100644 server.err diff --git a/capiorun/capiorun b/capiorun/capiorun index 5e236c872..44291b839 100755 --- a/capiorun/capiorun +++ b/capiorun/capiorun @@ -4,54 +4,100 @@ import argparse import json import os import signal -import socket import subprocess import sys import time - -################### -### Single Node ### -################### - -### Single App Setup ### -# Location: node1 -# Processes: capiorun1, server, app1 -# Events: -# - app1 completes execution -# - capiorun1 sends SIGUSR1 to the server -# - Server can terminate after handling the signal - -### Multi-App Setup ### -# Location: node1 -# Processes: capiorun1, capiorun2, server, app1, app2 -# Events: -# - app1 completes execution -# - capiorun1 does NOT send SIGUSR1 to the server if app2 is still running -# - app2 completes execution -# - capiorun2 sends SIGUSR1 to the server, signaling completion - - -################### -### Multi-Node ### -################### - -### Single App on Each Node ### -# Node 1 Node 2 -# Processes: capiorun1 capiorun2 -# server1 server2 -# app1 app2 -# Events: -# - app1 finishes execution -# - app2 consumes app1's output -# - server1 (Capio server) processes app2's request -# - capiorun1 sends SIGUSR1 to server1 -# - server1 completes the request and terminates -# - app2 finishes execution -# - capiorun2 sends SIGUSR1 to server2, signaling completion - - -def build_env(args, workflow_name, is_server=False): +parser = argparse.ArgumentParser( + prog="capiorun", + description=""" +capiorun - Simplified launcher for CAPIO-based applications. + +This utility streamlines the setup and execution of CAPIO workflows by automatically configuring +the environment and managing capio_server instances. It allows running specific application steps +defined in a CAPIO-CL configuration without manual setup of environment variables. + +Typical usage: + capiorun -d /mnt/capio -n myapp -c config.json -- +""", + epilog=""" +Developed by Marco Edoardo Santimaria + +For more information, refer to the CAPIO documentation or repository. +""", + formatter_class=argparse.RawTextHelpFormatter, +) + +# Required arguments +parser.add_argument( + "-d", + "--capio-dir", + required=True, + help="CAPIO virtual mount point (e.g., /mnt/capio)", +) +parser.add_argument( + "-n", + "--app-name", + required=True, + help="Name of the CAPIO application step to launch. Must match an entry in the CAPIO-CL config.", +) + +# Optional but commonly used +parser.add_argument( + "-c", + "--capiocl", + default="--no-config", + help="Path to the CAPIO-CL configuration file (default: --no-config)", +) +parser.add_argument( + "-m", "--metadata-dir", default="", help="Custom directory for metadata" +) + +# Debug and logging +parser.add_argument( + "-L", + "--log-level", + default="-1", + help="CAPIO log level. Useful when running in debug mode (default: -1)", +) +parser.add_argument( + "--log-dir", default="", help="Custom directory for CAPIO log output" +) +parser.add_argument("--log-prefix", default="", help="Prefix for CAPIO log files") + +# Tuning and advanced +parser.add_argument( + "--cache-lines", + default="", + help="Number of CAPIO shm-queue cache lines (optional tuning parameter)", +) +parser.add_argument( + "--init-file-size", + default="", + help="Default file size (in bytes) when pre-allocating memory for new files", +) + +# Binary locations +parser.add_argument( + "-l", + "--libcapio", + default="libcapio_posix.so", + help="Path to libcapio_posix.so shared library (default: libcapio_posix.so)", +) +parser.add_argument( + "-s", + "--server", + default="capio_server", + help="Path to capio_server executable (default: capio_server)", +) + +# Positional arguments +parser.add_argument( + "args", nargs=argparse.REMAINDER, help="Command to launch with capio" +) + + +def build_env(args, workflow_name): env = os.environ.copy() if args.log_dir: env["CAPIO_LOG_DIR"] = args.log_dir @@ -61,14 +107,12 @@ def build_env(args, workflow_name, is_server=False): env["CAPIO_CACHE_LINES"] = args.cache_lines if args.init_file_size: env["CAPIO_FILE_INIT_SIZE"] = args.init_file_size + env["CAPIO_DIR"] = args.capio_dir env["CAPIO_LOG_LEVEL"] = args.log_level env["CAPIO_WORKFLOW_NAME"] = workflow_name - if is_server: - env["CAPIO_METADATA_DIR"] = args.metadata_dir - else: # is a client - env["CAPIO_APP_NAME"] = args.app_name - env["LD_PRELOAD"] = args.libcapio + env["CAPIO_METADATA_DIR"] = args.metadata_dir + return env @@ -81,64 +125,62 @@ def count_files_starting_with(prefix): ) -def main(args): - server_process = None - step_process = None +server_process = None +step_process = None +if __name__ == "__main__": + args = parser.parse_args() + + workflow_name = "CAPIO" if args.capiocl != "--no-config": with open(args.capiocl, "r") as f: workflow_name = json.load(f)["name"] - else: - workflow_name = "CAPIO" if not os.path.exists(f"/dev/shm/{workflow_name}"): print(f"Starting capio server with config file: {args.capiocl}") - print(f"CAPIO_APP_NAME = {args.app_name}") - print(f"CAPIO_DIR = {args.capio_dir}") - print(f"CAPIO_CL_CONFIG = {args.capiocl}") - print(f"CAPIO_LOG_DIR = {args.log_dir}") print(f"CAPIO_LOG_LEVEL = {args.log_level}") - print(f"CAPIO_METADATA_DIR = {args.metadata_dir}") print(f"CAPIO_WORKFLOW_NAME = {workflow_name}") + print(f"CAPIO_APP_NAME = {args.app_name}") + print(f"CAPIO_DIR = {args.capio_dir}") + print(f"CAPIO-CL CONFIG = {args.capiocl}") if not os.path.exists(args.capiocl) and args.capiocl != "--no-config": print(f"File {args.capiocl} does not exists. aborting execution...") exit(1) - server_env = build_env(args, workflow_name, is_server=True) - - hostname = socket.gethostname() - pid = os.getpid() - with open( - os.path.join(args.log_dir, f"server_{hostname}_{pid}.out"), "w" - ) as out_file: - server_process = subprocess.Popen( - [ - args.server, - ( - "--config " + args.capiocl - if args.capiocl != "--no-config" - else args.capiocl - ), - ], - env=server_env, - cwd=args.log_dir, - #stdout=out_file, - #stderr=out_file, - start_new_session=True, - ) + server_env = build_env(args, workflow_name) + + server_process = subprocess.Popen( + [ + args.server, + ( + ("--config " + args.capiocl) + if args.capiocl != "--no-config" + else args.capiocl + ), + ], + env=server_env, + stdout="server.log", + stderr="server.err", + cwd=args.log_dir, + start_new_session=True, + ) - print(f"{hostname} capiorun-pid {pid} - capio_server PID: {server_process.pid}") + print(f"capio_server PID: {server_process.pid}") time.sleep(1) + else: print( f"An instance of capio_server with workflow name {workflow_name} already exists!" ) + step_env = build_env(args, workflow_name) + step_env["CAPIO_APP_NAME"] = args.app_name + step_env["LD_PRELOAD"] = args.libcapio print(f"Starting workflow steps with following environment variables:") - print(f"CAPIO_APP_NAME = {args.app_name}") - print(f"CAPIO_DIR = {args.capio_dir}") print(f"CAPIO_LOG_LEVEL = {args.log_level}") print(f"CAPIO_WORKFLOW_NAME = {workflow_name}") + print(f"CAPIO_APP_NAME = {args.app_name}") + print(f"CAPIO_DIR = {args.capio_dir}") print(f"LD_PRELOAD = {args.libcapio}") print(f"command = {' '.join(args.args)}") try: @@ -148,34 +190,31 @@ def main(args): stdout=sys.stdout, stderr=sys.stderr, ) + step_process.wait() - print( - f"Step {args.app_name} terminated with returncode:", step_process.returncode - ) + print(f"Step {args.app_name} terminated successfully") except Exception as e: print( f"An error occurred in startup/execution of workflow app <{args.app_name}>: {e}" ) if server_process is not None: - if count_files_starting_with(workflow_name) > 4: + if count_files_starting_with(workflow_name) >= 4: print( "Server instance is used by other applications... skipping server termination" ) else: print(f"Terminating instance of capio_server") - server_process.send_signal(signal.SIGUSR1) + server_process.send_signal(signal.SIGTERM) time.sleep(2) print("Terminated CAPIO server instance") else: - if count_files_starting_with(workflow_name) <= 4: + if count_files_starting_with(workflow_name) < 4: print( "Terminating instance of capio_server started by other capiorun command" ) result = subprocess.run( - ["killall", "-USR1", "capio_server"], - stdout=sys.stdout, - stderr=sys.stderr, + ["killall", "capio_server"], stdout=sys.stdout, stderr=sys.stderr ) if result.returncode == 0: print("Terminated CAPIO server instance") @@ -183,98 +222,3 @@ def main(args): print("Error terminating capio_server instance!") else: print("Skipping termination of capio_server") - - -def parse_args(): - parser = argparse.ArgumentParser( - prog="capiorun", - description=""" - capiorun - Simplified launcher for CAPIO-based applications. - - This utility streamlines the setup and execution of CAPIO workflows by automatically configuring - the environment and managing capio_server instances. It allows running specific application steps - defined in a CAPIO-CL configuration without manual setup of environment variables. - - Typical usage: - capiorun -d /mnt/capio -n myapp -c config.json -- - """, - epilog=""" - Developed by Marco Edoardo Santimaria - - For more information, refer to the CAPIO documentation or repository. - """, - formatter_class=argparse.RawTextHelpFormatter, - ) - - # Required arguments - parser.add_argument( - "-d", - "--capio-dir", - required=True, - help="CAPIO virtual mount point (e.g., /mnt/capio)", - ) - parser.add_argument( - "-n", - "--app-name", - required=True, - help="Name of the CAPIO application step to launch. Must match an entry in the CAPIO-CL config.", - ) - - # Optional but commonly used - parser.add_argument( - "-c", - "--capiocl", - default="--no-config", - help="Path to the CAPIO-CL configuration file (default: --no-config)", - ) - parser.add_argument( - "-m", "--metadata-dir", default="", help="Custom directory for metadata" - ) - - # Debug and logging - parser.add_argument( - "-L", - "--log-level", - default="-1", - help="CAPIO log level. Useful when running in debug mode (default: -1)", - ) - parser.add_argument( - "--log-dir", default="", help="Custom directory for CAPIO log output" - ) - parser.add_argument("--log-prefix", default="", help="Prefix for CAPIO log files") - - # Tuning and advanced - parser.add_argument( - "--cache-lines", - default="", - help="Number of CAPIO shm-queue cache lines (optional tuning parameter)", - ) - parser.add_argument( - "--init-file-size", - default="", - help="Default file size (in bytes) when pre-allocating memory for new files", - ) - - # Binary locations - parser.add_argument( - "-l", - "--libcapio", - default="libcapio_posix.so", - help="Path to libcapio_posix.so shared library (default: libcapio_posix.so)", - ) - parser.add_argument( - "-s", - "--server", - default="capio_server", - help="Path to capio_server executable (default: capio_server)", - ) - - # Positional arguments - parser.add_argument( - "args", nargs=argparse.REMAINDER, help="Command to launch with capio" - ) - return parser.parse_args() - - -if __name__ == "__main__": - main(parse_args()) diff --git a/capiorun/server.err b/capiorun/server.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/server.err b/server.err deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/server/client-manager/handlers/handshake.hpp b/src/server/client-manager/handlers/handshake.hpp index 9ba1f813e..37facceae 100644 --- a/src/server/client-manager/handlers/handshake.hpp +++ b/src/server/client-manager/handlers/handshake.hpp @@ -31,9 +31,7 @@ inline void handshake_handler(const char *const str) { LOG("Termination phase is in progress. ignoring further handshakes."); client_manager->reply_to_client(tid, 0); server_println(CAPIO_LOG_SERVER_CLI_LEVEL_ERROR, - "Termination phase is in progress. " - "ignoring further handshakes."); - + "Termination phase is in progress. Ignoring further handshakes."); } } From 0729496bef915a78c6c00747ac69926f8c21836e Mon Sep 17 00:00:00 2001 From: Marco Edoardo Santimaria Date: Thu, 28 Aug 2025 14:09:01 +0200 Subject: [PATCH 7/7] readme --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 4dede7032..2bd80b98d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Bash. > Just use a MTCL provided backend, if you want the in-memory IO, or fall back to the file system backend (default) if > oy just want to coordinate IO operations between workflow steps! +Compatible on: +- ![Architecture](https://img.shields.io/badge/Architecture-x86_64-blue.svg) +- ![Architecture](https://img.shields.io/badge/Architecture-risc--v-green.svg) +- ![Architecture](https://img.shields.io/badge/Architecture-arm64-red.svg) coming soon! --- ## Automatic install with SPACK @@ -22,6 +26,9 @@ spack repo add https://github.com/High-Performance-IO/hpio-spack.git spack install capio ``` +> [!WARNING] +> To use this method, you need spack >= v1.0.0 + ## 🔧 Manual Build and Install ### Dependencies