From 889f32f4576345662421b08f41026e0eb8343f2a Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Tue, 6 May 2025 20:26:38 +0530 Subject: [PATCH 01/10] removed redundant files in lite case --- CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index da8d5d76..cf0c64ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,8 +444,8 @@ if(EMSCRIPTEN) xeus_wasm_link_options(xcpp "web,worker") target_link_options(xcpp PUBLIC "SHELL: --preload-file ${SYSROOT_PATH}/include@/include" - PUBLIC "SHELL: --preload-file ${XEUS_CPP_DATA_DIR}@/share/xeus-cpp" - PUBLIC "SHELL: --preload-file ${XEUS_CPP_CONF_DIR}@/etc/xeus-cpp" + PUBLIC "SHELL: --preload-file ${XCPP_TAGFILES_DIR}@/share/xeus-cpp/tagfiles" + PUBLIC "SHELL: --preload-file ${XCPP_TAGCONFS_DIR}@/etc/xeus-cpp/tags.d" PUBLIC "SHELL: --post-js ${CMAKE_CURRENT_SOURCE_DIR}/wasm_patches/post.js" ) # TODO: Emscripten supports preloading files just once before it generates @@ -472,11 +472,13 @@ include(CMakePackageConfigHelpers) set(XEUS_CPP_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING "install path for xeus-cppConfig.cmake") -install(DIRECTORY ${XCPP_TAGFILES_DIR} - DESTINATION ${XEUS_CPP_DATA_DIR}) +if(NOT EMSCRIPTEN) + install(DIRECTORY ${XCPP_TAGFILES_DIR} + DESTINATION ${XEUS_CPP_DATA_DIR}) -install(DIRECTORY ${XCPP_TAGCONFS_DIR} - DESTINATION ${XEUS_CPP_CONF_DIR}) + install(DIRECTORY ${XCPP_TAGCONFS_DIR} + DESTINATION ${XEUS_CPP_CONF_DIR}) +endif() # Install xeus-cpp and xeus-cpp-static if (XEUS_CPP_BUILD_SHARED) From 3d11208c05bfbed84459258f45c406b5b28d2837 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Sat, 17 May 2025 14:23:18 +0530 Subject: [PATCH 02/10] initial lldb start --- .vscode/settings.json | 5 + CMakeLists.txt | 4 + include/xeus-cpp/xdebugger.hpp | 79 ++++ .../jupyter/kernels/xcpp17-omp/kernel.json.in | 2 +- share/jupyter/kernels/xcpp17/kernel.json.in | 2 +- .../kernels/xcpp17/wasm_kernel.json.in | 2 +- src/main.cpp | 70 ++-- src/xdebugger.cpp | 337 ++++++++++++++++++ src/xdebuglldb_client.cpp | 88 +++++ src/xdebuglldb_client.hpp | 36 ++ src/xinternal_utils.cpp | 79 ++++ src/xinternal_utils.hpp | 24 ++ 12 files changed, 687 insertions(+), 41 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 include/xeus-cpp/xdebugger.hpp create mode 100644 src/xdebugger.cpp create mode 100644 src/xdebuglldb_client.cpp create mode 100644 src/xdebuglldb_client.hpp create mode 100644 src/xinternal_utils.cpp create mode 100644 src/xinternal_utils.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0cba2e68 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "iostream": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index a7b9c48c..ec24a99c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,6 +193,7 @@ set(XEUS_CPP_HEADERS include/xeus-cpp/xmagics.hpp include/xeus-cpp/xoptions.hpp include/xeus-cpp/xpreamble.hpp + include/xeus-cpp/xdebugger.hpp #src/xinspect.hpp #src/xsystem.hpp #src/xparser.hpp @@ -207,6 +208,9 @@ set(XEUS_CPP_SRC src/xparser.cpp src/xutils.cpp src/xmagics/os.cpp + src/xdebugger.cpp + src/xinternal_utils.cpp + src/xdebuglldb_client.cpp ) if(NOT EMSCRIPTEN) diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp new file mode 100644 index 00000000..87d71725 --- /dev/null +++ b/include/xeus-cpp/xdebugger.hpp @@ -0,0 +1,79 @@ +/************************************************************************************ + * Copyright (c) 2023, xeus-cpp contributors * + * Copyright (c) 2023, Johan Mabille, Loic Gouarin, Sylvain Corlay, Wolf Vollprecht * + * * + * Distributed under the terms of the BSD 3-Clause License. * + * * + * The full license is in the file LICENSE, distributed with this software. * + ************************************************************************************/ + +#ifndef XEUS_CPP_DEBUGGER_HPP +#define XEUS_CPP_DEBUGGER_HPP + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wattributes" +#endif + +#include +#include +#include +#include + +#include "nlohmann/json.hpp" +#include "xeus-zmq/xdebugger_base.hpp" +#include "xeus_cpp_config.hpp" + +namespace xcpp +{ + class xdebuglldb_client; + + class XEUS_CPP_API debugger : public xeus::xdebugger_base + { + public: + + using base_type = xeus::xdebugger_base; + + debugger(xeus::xcontext& context, + const xeus::xconfiguration& config, + const std::string& user_name, + const std::string& session_id, + const nl::json& debugger_config); + + virtual ~debugger(); + + private: + + nl::json inspect_variables_request(const nl::json& message); + nl::json stack_trace_request(const nl::json& message); + nl::json attach_request(const nl::json& message); + nl::json configuration_done_request(const nl::json& message); + + nl::json variables_request_impl(const nl::json& message) override; + + bool start_lldb(); + bool start() override; + void stop() override; + xeus::xdebugger_info get_debugger_info() const override; + std::string get_cell_temporary_file(const std::string& code) const override; + + xdebuglldb_client* p_debuglldb_client; + std::string m_lldb_host; + std::string m_lldb_port; + nl::json m_debugger_config; + bool m_is_running; + }; + + XEUS_CPP_API + std::unique_ptr make_cpp_debugger(xeus::xcontext& context, + const xeus::xconfiguration& config, + const std::string& user_name, + const std::string& session_id, + const nl::json& debugger_config); +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif + +#endif \ No newline at end of file diff --git a/share/jupyter/kernels/xcpp17-omp/kernel.json.in b/share/jupyter/kernels/xcpp17-omp/kernel.json.in index f74379f1..6a3443b8 100644 --- a/share/jupyter/kernels/xcpp17-omp/kernel.json.in +++ b/share/jupyter/kernels/xcpp17-omp/kernel.json.in @@ -13,6 +13,6 @@ "-std=c++17"@XEUS_CPP_OMP@ ], "language": "cpp", - "metadata": {"debugger": false + "metadata": {"debugger": true } } diff --git a/share/jupyter/kernels/xcpp17/kernel.json.in b/share/jupyter/kernels/xcpp17/kernel.json.in index c09f6836..10b5b5d6 100644 --- a/share/jupyter/kernels/xcpp17/kernel.json.in +++ b/share/jupyter/kernels/xcpp17/kernel.json.in @@ -13,6 +13,6 @@ "-std=c++17" ], "language": "cpp", - "metadata": {"debugger": false + "metadata": {"debugger": true } } diff --git a/share/jupyter/kernels/xcpp17/wasm_kernel.json.in b/share/jupyter/kernels/xcpp17/wasm_kernel.json.in index a6be99dd..c9fe5b6f 100644 --- a/share/jupyter/kernels/xcpp17/wasm_kernel.json.in +++ b/share/jupyter/kernels/xcpp17/wasm_kernel.json.in @@ -7,7 +7,7 @@ ], "language": "cpp", "metadata": { - "debugger": false, + "debugger": true, "shared": { "libclangCppInterOp.so": "lib/libclangCppInterOp.so" } diff --git a/src/main.cpp b/src/main.cpp index 00d52d28..dffb2216 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,16 +19,18 @@ #include #endif +#include "nlohmann/json.hpp" #include "xeus/xhelper.hpp" -#include -#include - +#include "xeus/xkernel.hpp" +#include "xeus/xkernel_configuration.hpp" #include "xeus-zmq/xzmq_context.hpp" -#include - +#include "xeus-zmq/xserver_zmq.hpp" #include "xeus-cpp/xeus_cpp_config.hpp" #include "xeus-cpp/xinterpreter.hpp" #include "xeus-cpp/xutils.hpp" +#include "xeus-cpp/xdebugger.hpp" + +namespace nl = nlohmann; int main(int argc, char* argv[]) { @@ -58,6 +60,12 @@ int main(int argc, char* argv[]) #endif signal(SIGINT, xcpp::stop_handler); + // Debugger configuration for LLDB-DAP + nl::json debugger_config; + debugger_config["lldb"]["initCommands"] = { + "settings set plugin.jit-loader.gdb.enable on" + }; + std::string file_name = xeus::extract_filename(argc, argv); auto interpreter = std::make_unique(argc, argv); std::unique_ptr context = xeus::make_zmq_context(); @@ -77,13 +85,14 @@ int main(int argc, char* argv[]) xeus::make_console_logger( xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log") - ) + ), + xcpp::make_cpp_debugger, + debugger_config ); std::clog << "Starting xcpp kernel...\n\n" "If you want to connect to this kernel from an other client, you can use" - " the " - + file_name + " file." + " the " + file_name + " file." << std::endl; kernel.start(); @@ -99,7 +108,9 @@ int main(int argc, char* argv[]) xeus::make_console_logger( xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log") - ) + ), + xcpp::make_cpp_debugger, + debugger_config ); std::cout << "Getting config" << std::endl; @@ -109,37 +120,20 @@ int main(int argc, char* argv[]) " and paste the following content inside of a `kernel.json` file. And then run for example:\n\n" "# jupyter console --existing kernel.json\n\n" "kernel.json\n```\n{\n" - " \"transport\": \"" - + config.m_transport - + "\",\n" - " \"ip\": \"" - + config.m_ip - + "\",\n" - " \"control_port\": " - + config.m_control_port - + ",\n" - " \"shell_port\": " - + config.m_shell_port - + ",\n" - " \"stdin_port\": " - + config.m_stdin_port - + ",\n" - " \"iopub_port\": " - + config.m_iopub_port - + ",\n" - " \"hb_port\": " - + config.m_hb_port - + ",\n" - " \"signature_scheme\": \"" - + config.m_signature_scheme - + "\",\n" - " \"key\": \"" - + config.m_key - + "\"\n" - "}\n```\n"; + " \"transport\": \"" + config.m_transport + "\",\n" + " \"ip\": \"" + config.m_ip + "\",\n" + " \"control_port\": " + config.m_control_port + ",\n" + " \"shell_port\": " + config.m_shell_port + ",\n" + " \"stdin_port\": " + config.m_stdin_port + ",\n" + " \"iopub_port\": " + config.m_iopub_port + ",\n" + " \"hb_port\": " + config.m_hb_port + ",\n" + " \"signature_scheme\": \"" + config.m_signature_scheme + "\",\n" + " \"key\": \"" + config.m_key + "\"\n" + "}\n```" + << std::endl; kernel.start(); } return 0; -} +} \ No newline at end of file diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp new file mode 100644 index 00000000..89b1757d --- /dev/null +++ b/src/xdebugger.cpp @@ -0,0 +1,337 @@ +#include "xeus-cpp/xdebugger.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nlohmann/json.hpp" +#include "xdebuglldb_client.hpp" +#include "xeus-zmq/xmiddleware.hpp" +#include "xeus/xsystem.hpp" +#include "xinternal_utils.hpp" + +using namespace std::placeholders; + +namespace xcpp +{ + debugger::debugger( + xeus::xcontext& context, + const xeus::xconfiguration& config, + const std::string& user_name, + const std::string& session_id, + const nl::json& debugger_config + ) + : xdebugger_base(context) + , p_debuglldb_client(new xdebuglldb_client( + context, + config, + xeus::get_socket_linger(), + xdap_tcp_configuration(xeus::dap_tcp_type::client, xeus::dap_init_type::parallel, user_name, session_id), + get_event_callback() + )) + , m_lldb_host("127.0.0.1") + , m_lldb_port("") + , m_debugger_config(debugger_config) + , m_is_running(false) + { + // Register request handlers + register_request_handler( + "inspectVariables", + std::bind(&debugger::inspect_variables_request, this, _1), + false + ); + register_request_handler("stackTrace", std::bind(&debugger::stack_trace_request, this, _1), false); + register_request_handler("attach", std::bind(&debugger::attach_request, this, _1), true); + register_request_handler( + "configurationDone", + std::bind(&debugger::configuration_done_request, this, _1), + true + ); + + std::cout << "Debugger initialized with config: " << m_debugger_config.dump() << std::endl; + } + + debugger::~debugger() + { + delete p_debuglldb_client; + p_debuglldb_client = nullptr; + } + + bool debugger::start_lldb() + { + // Find a free port for LLDB-DAP + m_lldb_port = xeus::find_free_port(100, 9999, 10099); + if (m_lldb_port.empty()) + { + std::cout << "Failed to find a free port for LLDB-DAP" << std::endl; + return false; + } + + // Log debugger configuration if XEUS_LOG is set + if (std::getenv("XEUS_LOG") != nullptr) + { + std::ofstream out("xeus.log", std::ios_base::app); + out << "===== DEBUGGER CONFIG =====" << std::endl; + out << m_debugger_config.dump() << std::endl; + } + + // Construct LLDB-DAP command + std::string lldb_command = "lldb-dap --port " + m_lldb_port; + + std::cout << "lldb-dap command: " << lldb_command << std::endl; + + // Optionally, append additional configuration from m_debugger_config + auto it = m_debugger_config.find("lldb"); + if (it != m_debugger_config.end() && it->is_object()) + { + if (it->contains("initCommands")) + { + std::cout << "Adding init commands to lldb-dap command" << std::endl; + for (const auto& cmd : it->at("initCommands").get>()) + { + std::cout << "Adding command: " << cmd << std::endl; + lldb_command += " --init-command \"" + cmd + "\""; + } + } + } + + // Start LLDB-DAP process + // Note: Using std::system for simplicity; replace with a process library in production + std::string log_dir = xeus::get_temp_directory_path() + "/xcpp_debug_logs_" + + std::to_string(xeus::get_current_pid()); + xeus::create_directory(log_dir); + std::string log_file = log_dir + "/lldb-dap.log"; + + // Fork to start lldb-dap + pid_t pid = fork(); + if (pid == 0) + { // Child process + // Redirect stdin, stdout, stderr to /dev/null and log file + int fd = open("/dev/null", O_RDONLY); + dup2(fd, STDIN_FILENO); + close(fd); + + fd = open(log_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + close(fd); + + // Execute lldb-dap + execlp( + "lldb-dap", + "lldb-dap", + "--port", + m_lldb_port.c_str(), + "--init-command", + "settings set plugin.jit-loader.gdb.enable on", + NULL + ); + + // If execlp fails + std::cout << xcpp::red_text("execlp failed for lldb-dap") << std::endl; + exit(1); + } + else if (pid > 0) + { // Parent process + std::cout << xcpp::green_text("LLDB-DAP process started, PID: ") << pid << std::endl; + + // Check if process is running + int status; + if (waitpid(pid, &status, WNOHANG) != 0) + { + std::cout << xcpp::red_text("LLDB-DAP process exited early") << std::endl; + return false; + } + } + else + { + std::cout << xcpp::red_text("fork failed") << std::endl; + return false; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + + try + { + std::string endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; + std::cout << xcpp::blue_text("Testing connection to LLDB-DAP at ") << endpoint << std::endl; + if (!p_debuglldb_client->test_connection(endpoint)) + { + std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; + return false; + } + std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; + } + catch (const std::exception& e) + { + std::cout << xcpp::red_text("Exception while connecting to LLDB-DAP: ") << e.what() << std::endl; + return false; + } + + m_is_running = true; + return true; + } + + bool debugger::start() + { + // Create temporary directory for logs + std::cout << "Starting debugger..." << std::endl; + std::string temp_dir = xeus::get_temp_directory_path(); + std::string log_dir = temp_dir + "/xcpp_debug_logs_" + std::to_string(xeus::get_current_pid()); + xeus::create_directory(log_dir); + + // Start LLDB-DAP + static bool lldb_started = start_lldb(); + if (!lldb_started) + { + std::cout << "Failed to start LLDB-DAP" << std::endl; + return false; + } + + // Bind xeus debugger sockets + std::string controller_end_point = xeus::get_controller_end_point("debugger"); + std::string controller_header_end_point = xeus::get_controller_end_point("debugger_header"); + std::string publisher_end_point = xeus::get_publisher_end_point(); + + bind_sockets(controller_header_end_point, controller_end_point); + + // Start LLDB-DAP client in a separate thread + std::string lldb_endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; + std::thread client( + &xdebuglldb_client::start_debugger, + p_debuglldb_client, + lldb_endpoint, + publisher_end_point, + controller_end_point, + controller_header_end_point + ); + client.detach(); + + // Send initial DAP request to verify connection + send_recv_request("REQ"); + + // Create temporary folder for cell code + std::string tmp_folder = get_tmp_prefix(); + xeus::create_directory(tmp_folder); + + return true; + } + + // Dummy implementations for other methods + nl::json debugger::inspect_variables_request(const nl::json& message) + { + // Placeholder DAP response + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]}, + {"message", "inspectVariables not implemented"} + }; + return reply; + } + + nl::json debugger::stack_trace_request(const nl::json& message) + { + // Placeholder DAP response + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]}, + {"message", "stackTrace not implemented"}, + {"body", {{"stackFrames", {}}}} + }; + return reply; + } + + nl::json debugger::attach_request(const nl::json& message) + { + // Placeholder DAP response + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]}, + {"message", "attach not implemented"} + }; + return reply; + } + + nl::json debugger::configuration_done_request(const nl::json& message) + { + // Minimal DAP response to allow DAP workflow to proceed + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", true}, + {"command", message["command"]} + }; + return reply; + } + + nl::json debugger::variables_request_impl(const nl::json& message) + { + // Placeholder DAP response + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]}, + {"message", "variables not implemented"}, + {"body", {{"variables", {}}}} + }; + return reply; + } + + void debugger::stop() + { + // Placeholder: Log stop attempt + std::cout << "Debugger stop called" << std::endl; + std::string controller_end_point = xeus::get_controller_end_point("debugger"); + std::string controller_header_end_point = xeus::get_controller_end_point("debugger_header"); + unbind_sockets(controller_header_end_point, controller_end_point); + } + + xeus::xdebugger_info debugger::get_debugger_info() const + { + // Placeholder debugger info + return xeus::xdebugger_info( + xeus::get_tmp_hash_seed(), + get_tmp_prefix(), + get_tmp_suffix(), + true, // Supports exceptions + {"C++ Exceptions"}, + true // Supports breakpoints + ); + } + + std::string debugger::get_cell_temporary_file(const std::string& code) const + { + // Placeholder: Return a dummy temporary file path + std::string tmp_file = get_tmp_prefix() + "/cell_tmp.cpp"; + std::ofstream out(tmp_file); + out << code; + out.close(); + return tmp_file; + } + + std::unique_ptr make_cpp_debugger( + xeus::xcontext& context, + const xeus::xconfiguration& config, + const std::string& user_name, + const std::string& session_id, + const nl::json& debugger_config + ) + { + std::cout << "Creating C++ debugger" << std::endl; + return std::unique_ptr( + new debugger(context, config, user_name, session_id, debugger_config) + ); + } +} \ No newline at end of file diff --git a/src/xdebuglldb_client.cpp b/src/xdebuglldb_client.cpp new file mode 100644 index 00000000..b09f9693 --- /dev/null +++ b/src/xdebuglldb_client.cpp @@ -0,0 +1,88 @@ +#include "xdebuglldb_client.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "nlohmann/json.hpp" +#include "xeus/xmessage.hpp" + +namespace nl = nlohmann; + +namespace xcpp +{ + xdebuglldb_client::xdebuglldb_client(xeus::xcontext& context, + const xeus::xconfiguration& config, + int socket_linger, + const xdap_tcp_configuration& dap_config, + const event_callback& cb) + : base_type(context, config, socket_linger, dap_config, cb) + { + } + + bool xdebuglldb_client::test_connection(const std::string& endpoint) + { + // Parse endpoint (format: tcp://host:port) + std::string host = "127.0.0.1"; + std::string port_str = endpoint.substr(endpoint.find_last_of(':') + 1); + int port = std::stoi(port_str); + + // Create a TCP socket + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + return false; + } + + // Set up server address + struct sockaddr_in server_addr; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(port); + inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr); + + // Attempt to connect with a timeout + struct timeval timeout; + timeout.tv_sec = 2; // 2-second timeout + timeout.tv_usec = 0; + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + int result = connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)); + close(sock); + + return result == 0; + } + + void xdebuglldb_client::handle_event(nl::json message) + { + // Forward DAP events to the base class (e.g., "stopped" events from LLDB-DAP) + forward_event(std::move(message)); + } + + nl::json xdebuglldb_client::get_stack_frames(int thread_id, int seq) + { + // Construct a DAP stackTrace request + nl::json request = { + {"type", "request"}, + {"seq", seq}, + {"command", "stackTrace"}, + {"arguments", { + {"threadId", thread_id} + }} + }; + + // Send the request + send_dap_request(std::move(request)); + + // Wait for the response + nl::json reply = wait_for_message([](const nl::json& message) + { + return message["type"] == "response" && message["command"] == "stackTrace"; + }); + + return reply["body"]["stackFrames"]; + } +} \ No newline at end of file diff --git a/src/xdebuglldb_client.hpp b/src/xdebuglldb_client.hpp new file mode 100644 index 00000000..86db3d98 --- /dev/null +++ b/src/xdebuglldb_client.hpp @@ -0,0 +1,36 @@ +#ifndef XEUS_CPP_DEBUGLLDB_CLIENT_HPP +#define XEUS_CPP_DEBUGLLDB_CLIENT_HPP + +#include "xeus-zmq/xdap_tcp_client.hpp" + +namespace xcpp +{ + using xeus::xdap_tcp_client; + using xeus::xdap_tcp_configuration; + + class xdebuglldb_client : public xdap_tcp_client + { + public: + + using base_type = xdap_tcp_client; + using event_callback = base_type::event_callback; + + xdebuglldb_client(xeus::xcontext& context, + const xeus::xconfiguration& config, + int socket_linger, + const xdap_tcp_configuration& dap_config, + const event_callback& cb); + + virtual ~xdebuglldb_client() = default; + + // Test connection to LLDB-DAP server + bool test_connection(const std::string& endpoint); + + private: + + void handle_event(nl::json message) override; + nl::json get_stack_frames(int thread_id, int seq); + }; +} + +#endif \ No newline at end of file diff --git a/src/xinternal_utils.cpp b/src/xinternal_utils.cpp new file mode 100644 index 00000000..20974898 --- /dev/null +++ b/src/xinternal_utils.cpp @@ -0,0 +1,79 @@ +#include "xinternal_utils.hpp" + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace xcpp +{ + std::string red_text(const std::string& text) + { +#ifdef _WIN32 + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + GetConsoleScreenBufferInfo(hConsole, &consoleInfo); + SetConsoleTextAttribute(hConsole, FOREGROUND_RED); + std::string result = text; + SetConsoleTextAttribute(hConsole, consoleInfo.wAttributes); + return result; +#else + return "\033[0;31m" + text + "\033[0m"; +#endif + } + + std::string green_text(const std::string& text) + { +#ifdef _WIN32 + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + GetConsoleScreenBufferInfo(hConsole, &consoleInfo); + SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN); + std::string result = text; + SetConsoleTextAttribute(hConsole, consoleInfo.wAttributes); + return result; +#else + return "\033[0;32m" + text + "\033[0m"; +#endif + } + + std::string blue_text(const std::string& text) + { +#ifdef _WIN32 + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + GetConsoleScreenBufferInfo(hConsole, &consoleInfo); + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE); + std::string result = text; + SetConsoleTextAttribute(hConsole, consoleInfo.wAttributes); + return result; +#else + return "\033[0;34m" + text + "\033[0m"; +#endif + } + + std::string highlight(const std::string& code) + { + // Placeholder: No syntax highlighting implemented + // In a real implementation, use a C++ library (e.g., libclang or a custom highlighter) + return code; + } + + std::string get_tmp_prefix() + { + return xeus::get_tmp_prefix("xcpp"); + } + + std::string get_tmp_suffix() + { + return ".cpp"; + } + + std::string get_cell_tmp_file(const std::string& content) + { + return xeus::get_cell_tmp_file(get_tmp_prefix(), + content, + get_tmp_suffix()); + } +} \ No newline at end of file diff --git a/src/xinternal_utils.hpp b/src/xinternal_utils.hpp new file mode 100644 index 00000000..bf084172 --- /dev/null +++ b/src/xinternal_utils.hpp @@ -0,0 +1,24 @@ +#ifndef XEUS_CPP_INTERNAL_UTILS_HPP +#define XEUS_CPP_INTERNAL_UTILS_HPP + +#include + +#include "xeus/xsystem.hpp" + +namespace xcpp +{ + // Colorize text for terminal output + std::string red_text(const std::string& text); + std::string green_text(const std::string& text); + std::string blue_text(const std::string& text); + + // Placeholder for C++ syntax highlighting + std::string highlight(const std::string& code); + + // Temporary file utilities for Jupyter cells + std::string get_tmp_prefix(); + std::string get_tmp_suffix(); + std::string get_cell_tmp_file(const std::string& content); +} + +#endif \ No newline at end of file From 874fb8518c647b9997be0db8583255f80d794e2e Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Sat, 17 May 2025 18:49:19 +0530 Subject: [PATCH 03/10] sent init request while testing connection --- src/xdebuglldb_client.cpp | 104 ++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 22 deletions(-) diff --git a/src/xdebuglldb_client.cpp b/src/xdebuglldb_client.cpp index b09f9693..35a6558f 100644 --- a/src/xdebuglldb_client.cpp +++ b/src/xdebuglldb_client.cpp @@ -1,25 +1,28 @@ #include "xdebuglldb_client.hpp" +#include #include -#include +#include #include #include -#include -#include +#include #include #include "nlohmann/json.hpp" #include "xeus/xmessage.hpp" +#include namespace nl = nlohmann; namespace xcpp { - xdebuglldb_client::xdebuglldb_client(xeus::xcontext& context, - const xeus::xconfiguration& config, - int socket_linger, - const xdap_tcp_configuration& dap_config, - const event_callback& cb) + xdebuglldb_client::xdebuglldb_client( + xeus::xcontext& context, + const xeus::xconfiguration& config, + int socket_linger, + const xdap_tcp_configuration& dap_config, + const event_callback& cb + ) : base_type(context, config, socket_linger, dap_config, cb) { } @@ -29,7 +32,15 @@ namespace xcpp // Parse endpoint (format: tcp://host:port) std::string host = "127.0.0.1"; std::string port_str = endpoint.substr(endpoint.find_last_of(':') + 1); - int port = std::stoi(port_str); + int port; + try + { + port = std::stoi(port_str); + } + catch (...) + { + return false; // Invalid port + } // Create a TCP socket int sock = socket(AF_INET, SOCK_STREAM, 0); @@ -42,18 +53,67 @@ namespace xcpp struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); - inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr); + if (inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr) <= 0) + { + close(sock); + return false; + } - // Attempt to connect with a timeout + // Set socket timeout for connection struct timeval timeout; - timeout.tv_sec = 2; // 2-second timeout + timeout.tv_sec = 2; // 2-second timeout timeout.tv_usec = 0; setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); - int result = connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)); - close(sock); + // Attempt to connect + if (connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) != 0) + { + close(sock); + return false; + } + + // Prepare init_request (JSON string) + std::string init_request = R"({ + "seq": 1, + "type": "request", + "command": "initialize", + "arguments": { + "clientID": "manual-client", + "adapterID": "lldb", + "linesStartAt1": true, + "columnsStartAt1": true + } + })"; + + // Prepare DAP message with Content-Length header + std::ostringstream message_stream; + message_stream << "Content-Length: " << init_request.length() << "\r\n\r\n" << init_request; + std::string message = message_stream.str(); + + // Send the init_request + ssize_t bytes_sent = send(sock, message.c_str(), message.length(), 0); + if (bytes_sent != static_cast(message.length())) + { + close(sock); + return false; + } - return result == 0; + // Try to receive a response + char buffer[4096]; + ssize_t bytes_received = recv(sock, buffer, sizeof(buffer) - 1, 0); + if (bytes_received > 0) + { + buffer[bytes_received] = '\0'; // Null-terminate the received data + // Basic check: if we received any data, consider the connection successful + std::cout << "Received response: " << buffer << std::endl; + close(sock); + return true; + } + + // No response or error + close(sock); + return false; } void xdebuglldb_client::handle_event(nl::json message) @@ -69,19 +129,19 @@ namespace xcpp {"type", "request"}, {"seq", seq}, {"command", "stackTrace"}, - {"arguments", { - {"threadId", thread_id} - }} + {"arguments", {{"threadId", thread_id}}} }; // Send the request send_dap_request(std::move(request)); // Wait for the response - nl::json reply = wait_for_message([](const nl::json& message) - { - return message["type"] == "response" && message["command"] == "stackTrace"; - }); + nl::json reply = wait_for_message( + [](const nl::json& message) + { + return message["type"] == "response" && message["command"] == "stackTrace"; + } + ); return reply["body"]["stackFrames"]; } From 9bdb79c5a821548ce49fe572d034c2bc19988291 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Mon, 19 May 2025 18:33:38 +0530 Subject: [PATCH 04/10] removed testing connection --- src/xdebugger.cpp | 81 ++++++++++++++++++++++++++++----------- src/xdebuglldb_client.cpp | 10 ++--- 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 89b1757d..429861f0 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -127,8 +127,6 @@ namespace xcpp "lldb-dap", "--port", m_lldb_port.c_str(), - "--init-command", - "settings set plugin.jit-loader.gdb.enable on", NULL ); @@ -154,24 +152,42 @@ namespace xcpp return false; } - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - - try - { - std::string endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; - std::cout << xcpp::blue_text("Testing connection to LLDB-DAP at ") << endpoint << std::endl; - if (!p_debuglldb_client->test_connection(endpoint)) - { - std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; - return false; - } - std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; - } - catch (const std::exception& e) - { - std::cout << xcpp::red_text("Exception while connecting to LLDB-DAP: ") << e.what() << std::endl; - return false; - } + std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + + // try + // { + // std::string endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; + // std::cout << xcpp::blue_text("Testing connection to LLDB-DAP at ") << endpoint << std::endl; + // if (!p_debuglldb_client->test_connection(endpoint)) + // { + // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; + // return false; + // } + // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; + // if (!p_debuglldb_client->test_connection(endpoint)) + // { + // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; + // return false; + // } + // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; + // if (!p_debuglldb_client->test_connection(endpoint)) + // { + // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; + // return false; + // } + // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; + // if (!p_debuglldb_client->test_connection(endpoint)) + // { + // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; + // return false; + // } + // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; + // } + // catch (const std::exception& e) + // { + // std::cout << xcpp::red_text("Exception while connecting to LLDB-DAP: ") << e.what() << std::endl; + // return false; + // } m_is_running = true; return true; @@ -213,7 +229,23 @@ namespace xcpp client.detach(); // Send initial DAP request to verify connection - send_recv_request("REQ"); + + nl::json init_request = { + {"seq", 1}, + {"type", "request"}, + {"command", "initialize"}, + {"arguments", { + {"clientID", "manual-client"}, + {"adapterID", "lldb"}, + {"linesStartAt1", true}, + {"columnsStartAt1", true} + }} + }; + + + std::cout << send_recv_request("REQ") << std::endl; + nl::json request_reply = forward_message(init_request); + std::cout << request_reply.dump() << std::endl; // Create temporary folder for cell code std::string tmp_folder = get_tmp_prefix(); @@ -329,7 +361,12 @@ namespace xcpp const nl::json& debugger_config ) { - std::cout << "Creating C++ debugger" << std::endl; + std::clog << "Creating C++ debugger" << std::endl; + std::clog << "Debugger config: " << debugger_config.dump() << std::endl; + std::clog << "User name: " << user_name << std::endl; + std::clog << "Session ID: " << session_id << std::endl; + // std::cout << "Context: " << context.get_context_id() << std::endl; + // std::cout << "Config: " << config.dump() << std::endl; return std::unique_ptr( new debugger(context, config, user_name, session_id, debugger_config) ); diff --git a/src/xdebuglldb_client.cpp b/src/xdebuglldb_client.cpp index 35a6558f..fa11018c 100644 --- a/src/xdebuglldb_client.cpp +++ b/src/xdebuglldb_client.cpp @@ -55,7 +55,7 @@ namespace xcpp server_addr.sin_port = htons(port); if (inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr) <= 0) { - close(sock); + // close(sock); return false; } @@ -69,7 +69,7 @@ namespace xcpp // Attempt to connect if (connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) != 0) { - close(sock); + // close(sock); return false; } @@ -95,7 +95,7 @@ namespace xcpp ssize_t bytes_sent = send(sock, message.c_str(), message.length(), 0); if (bytes_sent != static_cast(message.length())) { - close(sock); + // close(sock); return false; } @@ -107,12 +107,12 @@ namespace xcpp buffer[bytes_received] = '\0'; // Null-terminate the received data // Basic check: if we received any data, consider the connection successful std::cout << "Received response: " << buffer << std::endl; - close(sock); + // close(sock); return true; } // No response or error - close(sock); + // close(sock); return false; } From d7c0df2a26d375df57def0811ec29de10b25f238 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Tue, 20 May 2025 12:33:27 +0530 Subject: [PATCH 05/10] multiple requests --- src/xdebugger.cpp | 122 +++++++++++++++++++++++++------------- src/xdebuglldb_client.cpp | 1 + src/xinterpreter.cpp | 1 + 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 429861f0..19bec4f8 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -58,6 +58,7 @@ namespace xcpp debugger::~debugger() { + std::cout << "Stopping debugger..........." << std::endl; delete p_debuglldb_client; p_debuglldb_client = nullptr; } @@ -152,43 +153,6 @@ namespace xcpp return false; } - std::this_thread::sleep_for(std::chrono::milliseconds(3000)); - - // try - // { - // std::string endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; - // std::cout << xcpp::blue_text("Testing connection to LLDB-DAP at ") << endpoint << std::endl; - // if (!p_debuglldb_client->test_connection(endpoint)) - // { - // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; - // return false; - // } - // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; - // if (!p_debuglldb_client->test_connection(endpoint)) - // { - // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; - // return false; - // } - // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; - // if (!p_debuglldb_client->test_connection(endpoint)) - // { - // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; - // return false; - // } - // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; - // if (!p_debuglldb_client->test_connection(endpoint)) - // { - // std::cout << xcpp::red_text("LLDB-DAP server not responding at ") << endpoint << std::endl; - // return false; - // } - // std::cout << xcpp::green_text("Connected to LLDB-DAP at ") << endpoint << std::endl; - // } - // catch (const std::exception& e) - // { - // std::cout << xcpp::red_text("Exception while connecting to LLDB-DAP: ") << e.what() << std::endl; - // return false; - // } - m_is_running = true; return true; } @@ -242,10 +206,79 @@ namespace xcpp }} }; + nl::json launch_request = { + {"seq", 2}, + {"type", "request"}, + {"command", "launch"}, + {"arguments", { + {"program", "/Users/abhinavkumar/Desktop/Coding/Testing/test"}, + {"args", {}}, + {"cwd", "/Users/abhinavkumar/Desktop/Coding/Testing"}, + {"initCommands", { + "settings set plugin.jit-loader.gdb.enable on" + }} + }} + }; + + nl::json breakpoint_request = { + {"seq", 3}, + {"type", "request"}, + {"command", "setBreakpoints"}, + {"arguments", { + {"source", { + {"name", "input_line_1"}, + {"path", "/Users/abhinavkumar/Desktop/Coding/Testing/input_line_1"} + }}, + {"breakpoints", { + {{"line", 8}} + }}, + {"lines", {8}}, + {"sourceModified", false} + }} + }; + + + + nl::json config_done_request = { + {"seq", 4}, + {"type", "request"}, + {"command", "configurationDone"}, + {"arguments", {}} + }; + + nl::json run_request = { + {"seq", 5}, + {"type", "request"}, + {"command", "continue"}, + {"arguments", {}} + }; + + nl::json stacktrace_request = { + {"seq", 6}, + {"type", "request"}, + {"command", "stackTrace"}, + {"arguments", { + {"threadId", 1}, + {"startFrame", 0}, + {"levels", 10} + }} + }; + std::cout << send_recv_request("REQ") << std::endl; nl::json request_reply = forward_message(init_request); std::cout << request_reply.dump() << std::endl; + request_reply = forward_message(launch_request); + std::cout << request_reply.dump() << std::endl; + request_reply = forward_message(breakpoint_request); + std::cout << request_reply.dump() << std::endl; + request_reply = forward_message(config_done_request); + std::cout << request_reply.dump() << std::endl; + request_reply = forward_message(run_request); + std::cout << request_reply.dump() << std::endl; + request_reply = forward_message(stacktrace_request); + std::cout << request_reply.dump() << std::endl; + // std::cout << send_recv_request("REQ") << std::endl; // Create temporary folder for cell code std::string tmp_folder = get_tmp_prefix(); @@ -258,6 +291,7 @@ namespace xcpp nl::json debugger::inspect_variables_request(const nl::json& message) { // Placeholder DAP response + std::cout << "inspect_variables_request not implemented" << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -271,6 +305,7 @@ namespace xcpp nl::json debugger::stack_trace_request(const nl::json& message) { // Placeholder DAP response + std::cout << "stack_trace_request not implemented" << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -285,6 +320,7 @@ namespace xcpp nl::json debugger::attach_request(const nl::json& message) { // Placeholder DAP response + std::cout << "attach_request not implemented" << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -298,6 +334,7 @@ namespace xcpp nl::json debugger::configuration_done_request(const nl::json& message) { // Minimal DAP response to allow DAP workflow to proceed + std::cout << "configuration_done_request not implemented" << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -310,6 +347,7 @@ namespace xcpp nl::json debugger::variables_request_impl(const nl::json& message) { // Placeholder DAP response + std::cout << "variables_request_impl not implemented" << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -333,6 +371,7 @@ namespace xcpp xeus::xdebugger_info debugger::get_debugger_info() const { // Placeholder debugger info + std::cout << "get_debugger_info called" << std::endl; return xeus::xdebugger_info( xeus::get_tmp_hash_seed(), get_tmp_prefix(), @@ -346,6 +385,7 @@ namespace xcpp std::string debugger::get_cell_temporary_file(const std::string& code) const { // Placeholder: Return a dummy temporary file path + std::cout << "get_cell_temporary_file called" << std::endl; std::string tmp_file = get_tmp_prefix() + "/cell_tmp.cpp"; std::ofstream out(tmp_file); out << code; @@ -361,10 +401,10 @@ namespace xcpp const nl::json& debugger_config ) { - std::clog << "Creating C++ debugger" << std::endl; - std::clog << "Debugger config: " << debugger_config.dump() << std::endl; - std::clog << "User name: " << user_name << std::endl; - std::clog << "Session ID: " << session_id << std::endl; + std::cout << "Creating C++ debugger" << std::endl; + std::cout << "Debugger config: " << debugger_config.dump() << std::endl; + std::cout << "User name: " << user_name << std::endl; + std::cout << "Session ID: " << session_id << std::endl; // std::cout << "Context: " << context.get_context_id() << std::endl; // std::cout << "Config: " << config.dump() << std::endl; return std::unique_ptr( diff --git a/src/xdebuglldb_client.cpp b/src/xdebuglldb_client.cpp index fa11018c..6eaee326 100644 --- a/src/xdebuglldb_client.cpp +++ b/src/xdebuglldb_client.cpp @@ -25,6 +25,7 @@ namespace xcpp ) : base_type(context, config, socket_linger, dap_config, cb) { + std::cout << "xdebuglldb_client initialized" << std::endl; } bool xdebuglldb_client::test_connection(const std::string& endpoint) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 14972324..75dc253d 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -312,6 +312,7 @@ __get_cxx_version () "\n" " xeus-cpp: a C++ Jupyter kernel - based on Clang-repl\n"; result["banner"] = banner; + result["debugger"] = true; result["language_info"]["name"] = "C++"; result["language_info"]["version"] = m_version; result["language_info"]["mimetype"] = "text/x-c++src"; From 346cf9fbbc22780380a1baed9b4fb2ff63921b8f Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 5 Jun 2025 22:57:46 +0530 Subject: [PATCH 06/10] attach_request --- include/xeus-cpp/xdebugger.hpp | 7 + include/xeus-cpp/xinterpreter.hpp | 2 + src/xdebugger.cpp | 354 +++++++++++++++++------------- src/xdebuglldb_client.cpp | 88 -------- src/xdebuglldb_client.hpp | 3 - src/xinterpreter.cpp | 105 +++++++++ 6 files changed, 319 insertions(+), 240 deletions(-) diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp index 87d71725..7fd8311f 100644 --- a/include/xeus-cpp/xdebugger.hpp +++ b/include/xeus-cpp/xdebugger.hpp @@ -57,11 +57,18 @@ namespace xcpp xeus::xdebugger_info get_debugger_info() const override; std::string get_cell_temporary_file(const std::string& code) const override; + bool connect_to_lldb_tcp(); + std::string send_dap_message(const nl::json& message); + std::string receive_dap_response(); + xdebuglldb_client* p_debuglldb_client; std::string m_lldb_host; std::string m_lldb_port; + std::string m_lldbdap_port; nl::json m_debugger_config; bool m_is_running; + int m_tcp_socket; + bool m_tcp_connected; }; XEUS_CPP_API diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index 25eeb049..40750e86 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -62,6 +62,8 @@ namespace xcpp void shutdown_request_impl() override; + nl::json internal_request_impl(const nl::json& content) override; + nl::json get_error_reply( const std::string& ename, const std::string& evalue, diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 19bec4f8..5c22211e 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -1,13 +1,17 @@ #include "xeus-cpp/xdebugger.hpp" +#include // For inet_pton(), htons() +#include // For std::chrono (used in sleep_for) #include +#include #include #include -#include +#include // For sockaddr_in, AF_INET +#include // For socket(), connect(), send(), recv() #include #include +#include #include -#include #include "nlohmann/json.hpp" #include "xdebuglldb_client.hpp" @@ -65,6 +69,8 @@ namespace xcpp bool debugger::start_lldb() { + std::cout << "debugger::start_lldb" << std::endl; + // Find a free port for LLDB-DAP m_lldb_port = xeus::find_free_port(100, 9999, 10099); if (m_lldb_port.empty()) @@ -81,12 +87,20 @@ namespace xcpp out << m_debugger_config.dump() << std::endl; } - // Construct LLDB-DAP command - std::string lldb_command = "lldb-dap --port " + m_lldb_port; - - std::cout << "lldb-dap command: " << lldb_command << std::endl; - - // Optionally, append additional configuration from m_debugger_config + // Build C++ code to start LLDB-DAP process + std::string code = "#include \n"; + code += "#include \n"; + code += "#include \n"; + code += "#include \n"; + code += "#include \n"; + code += "#include \n"; + code += "#include \n"; + code += "using namespace std;\n\n"; + code += "int main() {\n"; + + // Construct LLDB-DAP command arguments + code += " vector lldb_args = {\"lldb-dap\", \"--port\", \"" + m_lldb_port + "\"};\n"; + // Add additional configuration from m_debugger_config auto it = m_debugger_config.find("lldb"); if (it != m_debugger_config.end() && it->is_object()) { @@ -96,91 +110,148 @@ namespace xcpp for (const auto& cmd : it->at("initCommands").get>()) { std::cout << "Adding command: " << cmd << std::endl; - lldb_command += " --init-command \"" + cmd + "\""; + // Escape quotes in the command for C++ string + std::string escaped_cmd = cmd; + size_t pos = 0; + while ((pos = escaped_cmd.find("\"", pos)) != std::string::npos) + { + escaped_cmd.replace(pos, 1, "\\\""); + pos += 2; + } + while ((pos = escaped_cmd.find("\\", pos)) != std::string::npos + && pos < escaped_cmd.length() - 1) + { + if (escaped_cmd[pos + 1] != '\"') + { + escaped_cmd.replace(pos, 1, "\\\\"); + pos += 2; + } + else + { + pos += 2; + } + } + code += " lldb_args.push_back(\"--init-command\");\n"; + code += " lldb_args.push_back(\"" + escaped_cmd + "\");\n"; } } } - // Start LLDB-DAP process - // Note: Using std::system for simplicity; replace with a process library in production + // Set up log directory and file std::string log_dir = xeus::get_temp_directory_path() + "/xcpp_debug_logs_" + std::to_string(xeus::get_current_pid()); xeus::create_directory(log_dir); std::string log_file = log_dir + "/lldb-dap.log"; - // Fork to start lldb-dap - pid_t pid = fork(); - if (pid == 0) - { // Child process - // Redirect stdin, stdout, stderr to /dev/null and log file - int fd = open("/dev/null", O_RDONLY); - dup2(fd, STDIN_FILENO); - close(fd); - - fd = open(log_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - close(fd); - - // Execute lldb-dap - execlp( - "lldb-dap", - "lldb-dap", - "--port", - m_lldb_port.c_str(), - NULL - ); - - // If execlp fails - std::cout << xcpp::red_text("execlp failed for lldb-dap") << std::endl; - exit(1); - } - else if (pid > 0) - { // Parent process - std::cout << xcpp::green_text("LLDB-DAP process started, PID: ") << pid << std::endl; - - // Check if process is running - int status; - if (waitpid(pid, &status, WNOHANG) != 0) + // Add code to start the subprocess with proper redirection + code += " string log_file = \"" + log_file + "\";\n"; + code += " \n"; + code += " pid_t pid = fork();\n"; + code += " if (pid == 0) {\n"; + code += " // Child process - redirect stdout/stderr to log file\n"; + code += " int fd = open(log_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);\n"; + code += " if (fd != -1) {\n"; + code += " dup2(fd, STDOUT_FILENO);\n"; + code += " dup2(fd, STDERR_FILENO);\n"; + code += " close(fd);\n"; + code += " }\n"; + code += " \n"; + code += " // Redirect stdin to /dev/null\n"; + code += " int null_fd = open(\"/dev/null\", O_RDONLY);\n"; + code += " if (null_fd != -1) {\n"; + code += " dup2(null_fd, STDIN_FILENO);\n"; + code += " close(null_fd);\n"; + code += " }\n"; + code += " \n"; + code += " // Convert vector to char* array for execvp\n"; + code += " vector args;\n"; + code += " for (auto& arg : lldb_args) {\n"; + code += " args.push_back(const_cast(arg.c_str()));\n"; + code += " }\n"; + code += " args.push_back(nullptr);\n"; + code += " \n"; + code += " execvp(\"lldb-dap\", args.data());\n"; + code += " \n"; + code += " // If execvp fails\n"; + code += " cerr << \"Failed to execute lldb-dap\" << endl;\n"; + code += " exit(1);\n"; + code += " }\n"; + code += " else if (pid > 0) {\n"; + code += " // Parent process\n"; + code += " cout << \"LLDB-DAP process started, PID: \" << pid << endl;\n"; + code += " \n"; + code += " // Check if process is still running\n"; + code += " int status;\n"; + code += " if (waitpid(pid, &status, WNOHANG) != 0) {\n"; + code += " cerr << \"LLDB-DAP process exited early\" << endl;\n"; + code += " return 1;\n"; + code += " }\n"; + code += " \n"; + code += " cout << \"LLDB-DAP started successfully\" << endl;\n"; + code += " }\n"; + code += " else {\n"; + code += " cerr << \"fork() failed\" << endl;\n"; + code += " return 1;\n"; + code += " }\n"; + code += " \n"; + code += " return 0;\n"; + code += "}\n"; + + std::cout << "Starting LLDB-DAP with port: " << m_lldb_port << std::endl; + + // Execute the C++ code via control messenger + nl::json json_code; + json_code["code"] = code; + nl::json rep = xdebugger::get_control_messenger().send_to_shell(json_code); + std::string status = rep["status"].get(); + + std::cout << "LLDB-DAP start response: " << rep.dump() << std::endl; + + if (status != "ok") + { + std::string ename = rep["ename"].get(); + std::string evalue = rep["evalue"].get(); + std::vector traceback = rep["traceback"].get>(); + std::clog << "Exception raised when trying to start LLDB-DAP" << std::endl; + for (std::size_t i = 0; i < traceback.size(); ++i) { - std::cout << xcpp::red_text("LLDB-DAP process exited early") << std::endl; - return false; + std::clog << traceback[i] << std::endl; } + std::clog << ename << " - " << evalue << std::endl; + return false; } else { - std::cout << xcpp::red_text("fork failed") << std::endl; - return false; + std::cout << xcpp::green_text("LLDB-DAP process started successfully") << std::endl; } m_is_running = true; - return true; + return status == "ok"; } bool debugger::start() { - // Create temporary directory for logs std::cout << "Starting debugger..." << std::endl; - std::string temp_dir = xeus::get_temp_directory_path(); - std::string log_dir = temp_dir + "/xcpp_debug_logs_" + std::to_string(xeus::get_current_pid()); - xeus::create_directory(log_dir); - // Start LLDB-DAP + // Start LLDB-DAP process static bool lldb_started = start_lldb(); if (!lldb_started) { std::cout << "Failed to start LLDB-DAP" << std::endl; return false; } - - // Bind xeus debugger sockets + // Bind xeus debugger sockets for Jupyter communication std::string controller_end_point = xeus::get_controller_end_point("debugger"); std::string controller_header_end_point = xeus::get_controller_end_point("debugger_header"); std::string publisher_end_point = xeus::get_publisher_end_point(); - bind_sockets(controller_header_end_point, controller_end_point); - // Start LLDB-DAP client in a separate thread + std::cout << "Debugger sockets bound to: " << controller_end_point << std::endl; + std::cout << "Debugger header sockets bound to: " << controller_header_end_point << std::endl; + std::cout << "Publisher sockets bound to: " << publisher_end_point << std::endl; + std::cout << "LLDB-DAP host: " << m_lldb_host << ", port: " << m_lldb_port << std::endl; + + // Start LLDB-DAP client thread (for ZMQ communication) std::string lldb_endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; std::thread client( &xdebuglldb_client::start_debugger, @@ -192,93 +263,27 @@ namespace xcpp ); client.detach(); - // Send initial DAP request to verify connection - + // Now test direct TCP communication nl::json init_request = { {"seq", 1}, {"type", "request"}, {"command", "initialize"}, - {"arguments", { - {"clientID", "manual-client"}, - {"adapterID", "lldb"}, - {"linesStartAt1", true}, - {"columnsStartAt1", true} - }} - }; - - nl::json launch_request = { - {"seq", 2}, - {"type", "request"}, - {"command", "launch"}, - {"arguments", { - {"program", "/Users/abhinavkumar/Desktop/Coding/Testing/test"}, - {"args", {}}, - {"cwd", "/Users/abhinavkumar/Desktop/Coding/Testing"}, - {"initCommands", { - "settings set plugin.jit-loader.gdb.enable on" - }} - }} + {"arguments", + {{"adapterID", "xcpp17"}, + {"clientID", "jupyterlab"}, + {"clientName", "JupyterLab"}, + {"columnsStartAt1", true}, + {"linesStartAt1", true}, + {"locale", "en"}, + {"pathFormat", "path"}, + {"supportsRunInTerminalRequest", true}, + {"supportsVariablePaging", true}, + {"supportsVariableType", true}}} }; + // Also test ZMQ path + send_recv_request("REQ"); - nl::json breakpoint_request = { - {"seq", 3}, - {"type", "request"}, - {"command", "setBreakpoints"}, - {"arguments", { - {"source", { - {"name", "input_line_1"}, - {"path", "/Users/abhinavkumar/Desktop/Coding/Testing/input_line_1"} - }}, - {"breakpoints", { - {{"line", 8}} - }}, - {"lines", {8}}, - {"sourceModified", false} - }} - }; - - - - nl::json config_done_request = { - {"seq", 4}, - {"type", "request"}, - {"command", "configurationDone"}, - {"arguments", {}} - }; - - nl::json run_request = { - {"seq", 5}, - {"type", "request"}, - {"command", "continue"}, - {"arguments", {}} - }; - - nl::json stacktrace_request = { - {"seq", 6}, - {"type", "request"}, - {"command", "stackTrace"}, - {"arguments", { - {"threadId", 1}, - {"startFrame", 0}, - {"levels", 10} - }} - }; - - - std::cout << send_recv_request("REQ") << std::endl; - nl::json request_reply = forward_message(init_request); - std::cout << request_reply.dump() << std::endl; - request_reply = forward_message(launch_request); - std::cout << request_reply.dump() << std::endl; - request_reply = forward_message(breakpoint_request); - std::cout << request_reply.dump() << std::endl; - request_reply = forward_message(config_done_request); - std::cout << request_reply.dump() << std::endl; - request_reply = forward_message(run_request); - std::cout << request_reply.dump() << std::endl; - request_reply = forward_message(stacktrace_request); - std::cout << request_reply.dump() << std::endl; - // std::cout << send_recv_request("REQ") << std::endl; + // std::cout << forward_message(init_request).dump() << std::endl; // Create temporary folder for cell code std::string tmp_folder = get_tmp_prefix(); @@ -291,13 +296,59 @@ namespace xcpp nl::json debugger::inspect_variables_request(const nl::json& message) { // Placeholder DAP response + // std::cout << "Sending setBreakpoints request..." << std::endl; + // nl::json breakpoint_request = { + // {"seq", 3}, + // {"type", "request"}, + // {"command", "setBreakpoints"}, + // {"arguments", { + // {"source", { + // {"name", "input_line_1"}, + // {"path", "/Users/abhinavkumar/Desktop/Coding/Testing/input_line_1"} + // }}, + // {"breakpoints", {{{"line", 8}}}}, + // {"lines", {8}}, + // {"sourceModified", false} + // }} + // }; + // nl::json breakpoint_reply = forward_message(breakpoint_request); + // std::cout << "Breakpoint reply: " << breakpoint_reply.dump() << std::endl; + // nl::json config_done_request = { + // {"seq", 4}, + // {"type", "request"}, + // {"command", "configurationDone"} + // }; + // nl::json config_reply = forward_message(config_done_request); + // std::cout << "Configuration done reply: " << config_reply.dump() << std::endl; + + // nl::json run_request = { + // {"seq", 5}, + // {"type", "request"}, + // {"command", "continue"}, + // {"arguments", nl::json::object()} + // }; + // nl::json run_reply = forward_message(run_request); + // std::cout << "Continue reply: " << run_reply.dump() << std::endl; + std::cout << "inspect_variables_request not implemented" << std::endl; + std::cout << message.dump() << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, - {"success", false}, + {"success", true}, {"command", message["command"]}, - {"message", "inspectVariables not implemented"} + {"body", + {{"variables", + {{{"name", "a"}, + {"value", "100"}, + {"type", "int"}, + {"evaluateName", "a"}, + {"variablesReference", 0}}, + {{"name", "b"}, + {"value", "1000"}, + {"type", "int"}, + {"evaluateName", "b"}, + {"variablesReference", 0}}}}}} }; return reply; } @@ -320,14 +371,19 @@ namespace xcpp nl::json debugger::attach_request(const nl::json& message) { // Placeholder DAP response - std::cout << "attach_request not implemented" << std::endl; - nl::json reply = { - {"type", "response"}, - {"request_seq", message["seq"]}, - {"success", false}, - {"command", message["command"]}, - {"message", "attach not implemented"} + std::cout << "debugger::attach_request (sending launch instead)" << std::endl; + nl::json launch_request = { + {"seq", 2}, + {"type", "request"}, + {"command", "launch"}, + {"arguments", + {{"program", "/Users/abhinavkumar/Desktop/Coding/Testing/test"}, + {"args", {"arg1", "arg2"}}, + {"cwd", "/Users/abhinavkumar/Desktop/Coding/Testing"}, + {"initCommands", {"settings set plugin.jit-loader.gdb.enable on"}}}} }; + nl::json reply = forward_message(launch_request); + std::cout << "Launch request sent instead of attach: " << reply.dump() << std::endl; return reply; } diff --git a/src/xdebuglldb_client.cpp b/src/xdebuglldb_client.cpp index 6eaee326..e914ac5b 100644 --- a/src/xdebuglldb_client.cpp +++ b/src/xdebuglldb_client.cpp @@ -28,94 +28,6 @@ namespace xcpp std::cout << "xdebuglldb_client initialized" << std::endl; } - bool xdebuglldb_client::test_connection(const std::string& endpoint) - { - // Parse endpoint (format: tcp://host:port) - std::string host = "127.0.0.1"; - std::string port_str = endpoint.substr(endpoint.find_last_of(':') + 1); - int port; - try - { - port = std::stoi(port_str); - } - catch (...) - { - return false; // Invalid port - } - - // Create a TCP socket - int sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - { - return false; - } - - // Set up server address - struct sockaddr_in server_addr; - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - if (inet_pton(AF_INET, host.c_str(), &server_addr.sin_addr) <= 0) - { - // close(sock); - return false; - } - - // Set socket timeout for connection - struct timeval timeout; - timeout.tv_sec = 2; // 2-second timeout - timeout.tv_usec = 0; - setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); - - // Attempt to connect - if (connect(sock, (struct sockaddr*) &server_addr, sizeof(server_addr)) != 0) - { - // close(sock); - return false; - } - - // Prepare init_request (JSON string) - std::string init_request = R"({ - "seq": 1, - "type": "request", - "command": "initialize", - "arguments": { - "clientID": "manual-client", - "adapterID": "lldb", - "linesStartAt1": true, - "columnsStartAt1": true - } - })"; - - // Prepare DAP message with Content-Length header - std::ostringstream message_stream; - message_stream << "Content-Length: " << init_request.length() << "\r\n\r\n" << init_request; - std::string message = message_stream.str(); - - // Send the init_request - ssize_t bytes_sent = send(sock, message.c_str(), message.length(), 0); - if (bytes_sent != static_cast(message.length())) - { - // close(sock); - return false; - } - - // Try to receive a response - char buffer[4096]; - ssize_t bytes_received = recv(sock, buffer, sizeof(buffer) - 1, 0); - if (bytes_received > 0) - { - buffer[bytes_received] = '\0'; // Null-terminate the received data - // Basic check: if we received any data, consider the connection successful - std::cout << "Received response: " << buffer << std::endl; - // close(sock); - return true; - } - - // No response or error - // close(sock); - return false; - } void xdebuglldb_client::handle_event(nl::json message) { diff --git a/src/xdebuglldb_client.hpp b/src/xdebuglldb_client.hpp index 86db3d98..3ea029d6 100644 --- a/src/xdebuglldb_client.hpp +++ b/src/xdebuglldb_client.hpp @@ -23,9 +23,6 @@ namespace xcpp virtual ~xdebuglldb_client() = default; - // Test connection to LLDB-DAP server - bool test_connection(const std::string& endpoint); - private: void handle_event(nl::json message) override; diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 75dc253d..d5966b13 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -331,6 +331,111 @@ __get_cxx_version () restore_output(); } + nl::json interpreter::internal_request_impl(const nl::json& content) +{ + std::string code = content.value("code", ""); + nl::json reply; + std::cout << "Executing internal request with code:\n" << code << std::endl; + try + { + // Create temporary files for compilation + std::string temp_dir = xeus::get_temp_directory_path(); + std::string temp_source = temp_dir + "/xcpp_temp_" + std::to_string(xeus::get_current_pid()) + ".cpp"; + std::string temp_executable = temp_dir + "/xcpp_temp_" + std::to_string(xeus::get_current_pid()); + std::string temp_output = temp_dir + "/xcpp_output_" + std::to_string(xeus::get_current_pid()) + ".txt"; + std::string temp_error = temp_dir + "/xcpp_error_" + std::to_string(xeus::get_current_pid()) + ".txt"; + + // Write C++ code to temporary file + std::ofstream source_file(temp_source); + if (!source_file.is_open()) + { + throw std::runtime_error("Failed to create temporary source file"); + } + + source_file << code; + source_file.close(); + + // Compile the C++ code using clang++ + std::string compile_cmd = "clang++ " + temp_source + + " -o " + temp_executable + " 2>" + temp_error; + + int compile_result = std::system(compile_cmd.c_str()); + + if (compile_result != 0) + { + // Compilation failed - read error messages + std::ifstream error_file(temp_error); + std::string error_msg; + std::string line; + while (std::getline(error_file, line)) + { + error_msg += line + "\n"; + } + error_file.close(); + + // Clean up temporary files + std::remove(temp_source.c_str()); + std::remove(temp_error.c_str()); + + reply["status"] = "error"; + reply["ename"] = "CompilationError"; + reply["evalue"] = "C++ compilation failed"; + reply["traceback"] = std::vector{error_msg}; + return reply; + } + + // Execute the compiled program + std::string execute_cmd = temp_executable + " >" + temp_output + " 2>" + temp_error; + int execute_result = std::system(execute_cmd.c_str()); + + // Read output + std::ifstream output_file(temp_output); + std::string output; + std::string line; + while (std::getline(output_file, line)) + { + output += line + "\n"; + } + output_file.close(); + + // Read errors + std::ifstream error_file(temp_error); + std::string error_output; + while (std::getline(error_file, line)) + { + error_output += line + "\n"; + } + error_file.close(); + + // Clean up temporary files + std::remove(temp_source.c_str()); + std::remove(temp_executable.c_str()); + std::remove(temp_output.c_str()); + std::remove(temp_error.c_str()); + + if (execute_result != 0) + { + reply["status"] = "error"; + reply["ename"] = "RuntimeError"; + reply["evalue"] = "C++ program execution failed"; + reply["traceback"] = std::vector{error_output}; + } + else + { + reply["status"] = "ok"; + } + } + catch (const std::exception& e) + { + reply["status"] = "error"; + reply["ename"] = "SystemError"; + reply["evalue"] = e.what(); + reply["traceback"] = std::vector{e.what()}; + } + + return reply; +} + void interpreter::redirect_output() { p_cout_strbuf = std::cout.rdbuf(); From 609a590f05ea135016995aa9dc31b6ff9e64c9a4 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Mon, 9 Jun 2025 10:08:59 +0530 Subject: [PATCH 07/10] trying to attach --- CMakeLists.txt | 1 + environment-dev.yml | 2 +- .../kernels/xcpp17-debugger/kernel.json.in | 20 ++++++++++++++ .../kernels/xcpp17-debugger/logo-32x32.png | Bin 0 -> 1520 bytes .../kernels/xcpp17-debugger/logo-64x64.png | Bin 0 -> 3113 bytes .../kernels/xcpp17-debugger/logo-svg.svg | 25 ++++++++++++++++++ 6 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 share/jupyter/kernels/xcpp17-debugger/kernel.json.in create mode 100644 share/jupyter/kernels/xcpp17-debugger/logo-32x32.png create mode 100644 share/jupyter/kernels/xcpp17-debugger/logo-64x64.png create mode 100644 share/jupyter/kernels/xcpp17-debugger/logo-svg.svg diff --git a/CMakeLists.txt b/CMakeLists.txt index ec24a99c..eb84445b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,7 @@ else() configure_native_kernel("/share/jupyter/kernels/xcpp17/") configure_native_kernel("/share/jupyter/kernels/xcpp20/") configure_native_kernel("/share/jupyter/kernels/xcpp23/") + configure_native_kernel("/share/jupyter/kernels/xcpp17-debugger/") endif() # Source files diff --git a/environment-dev.yml b/environment-dev.yml index af545d32..6d919c4f 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -1,4 +1,4 @@ -name: xeus-cpp +name: xeus-cpp-2 channels: - conda-forge dependencies: diff --git a/share/jupyter/kernels/xcpp17-debugger/kernel.json.in b/share/jupyter/kernels/xcpp17-debugger/kernel.json.in new file mode 100644 index 00000000..1f1d839e --- /dev/null +++ b/share/jupyter/kernels/xcpp17-debugger/kernel.json.in @@ -0,0 +1,20 @@ +{ + "display_name": "C++17-Debugger", + "env": { + "PATH":"@XEUS_CPP_PATH@", + "LD_LIBRARY_PATH":"@XEUS_CPP_LD_LIBRARY_PATH@" + }, + "argv": [ + "@XEUS_CPP_KERNELSPEC_PATH@xcpp", + "-f", + "{connection_file}", + "-gdwarf-4", + "-O0", + "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", + "-I", "@XEUS_CPP_INCLUDE_DIR@", + "-std=c++17" + ], + "language": "cpp", + "metadata": {"debugger": true + } +} diff --git a/share/jupyter/kernels/xcpp17-debugger/logo-32x32.png b/share/jupyter/kernels/xcpp17-debugger/logo-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..c09c458544bb83b9e3ff4d38f5758bda10ceebe8 GIT binary patch literal 1520 zcmV`fj+1!di&IS(aK;ELYw|L~OnvC0l61ks3o346fp`H$NMM0omj!m; z_kH@H6w9)^K>k0T=Y7uqf1dN4=RHR-3_SCt%^Kb<)&MU7;Zlg5y2-oY^X2I+L-i$w zx?#n~wk$=V07O#1r4C?Ac(>)lg4HQKH(Wq=p)FksC6OXrNjh{uNT#gE{aMoakX3XTk{(FnV(Oe z@2s~w9%t9`^9n$i$CVI>+-wrO`tbNclf$)>dTY@HMq2(mOewM3E;b(gqU`v@`quU| z&?f^Z0J=#DSPf5QBrs{LbN<3m*yuT1l|^h>)_7v6XNK(hD<51m$Gp!i2?Hs zFyZD1GE)5`@AeoRtZC!y-<<#?jkYj5HI~Q-Uw67;ILoSQ?bVI#nSnTh3V0+f&aX}z z+PcZ#S4T&u_g`COos*JNt-LrdktqoyXmNVH?|fTz?!E6TCllfg2`C^uOkv8n5k5AL zX0Ua?FL^KAJv!TdtaGQ2w_aYgqb#wjN1GgsIHZ82Q5MW5-}n!o?V!!&PYxJuo$f<> zZ8eh$O1vq(#>ehTqQ`6S!^ukkJbCX#nw?#gRyBqe5M@>UY!{l{eztCpX4`+NHoVz+ zHKVWnvAdF3yXa0lngKwhMP*IaRN7oU2;jh}#^3@%K}wpARsDJXWX;)VB4aevn}+2l zpa91Jd7cm#!S6Ng?hBV1Dt@V`yj#=sYf*RD4b1BAM2(t+qKpCfrlKCeCj~+XzZ58e z1k|^7q3OO4>OINP#*q^h&I5;z&CxX7(q~CQ^Jc{Kv|Ha^kXjq2Dm}d-ddUHHm!I+vmF=K3XAm4o<09I^2 zovavoZU4pOcuU)x3zK8}|9GytJ#61w$!}E`0hlpqEXyC7PJFCy*_NHU=vA?5WbU2gZGAIi!T98TAL0IwA6BY9jbbEn@Hip&RR8tgp2sTe~_ zGnzLO`;~SgtmRlEX96Y^DdGAZ@Pv)Htm)c`((;OIbtZ_{%Kv7I2LIcHiD8=sv+=F~(6 z`hlLPZQ{M+!|eH`3K_H`VKb$<8+-)-%*{*1Bty1janV+0-F^$Vj)^2pHPK{uakS<# z=Nj9CZ3C!8QF17GyR6qRa33%~Z$8L;U`i;DgM=L+@`z5|N^y{;)CV&PRbrc0A?5#S z%MolthUIc#tz)1MLQh8bZyb%PtVc5R!9*vApL2&dnE2s=jp-=jBM>1*GtMBz84XAq=sq3n3 z^=Q_!)g~c!+q~quu9-OAo?0!V)>cdGqFI(tTeg~MXdzxOyl`DEa#8LB3@`(8pL4$b z13|_am@{Vvvfp1b=X<~Rd(ZED=l$OIJKsz2D`4pd1@kDuE0oYlghEOvN&xIPd;9PD zBq_!?n6{9?9PGKhd1FK@*#Tf~A0oAs&~@ocChP|xbTDih6arrScYcsnj5PpO0`TV1 zC6nQpH(i`_YZ9-xF-ot_e>8VLJ<0VH7Q2Zb{z z;nDz1a8p$~fH!@NzG?qjHp=7y(`?{z0+K!|h!k77tpIt(!>Y>wvTD!LxFeoa>M;VI z*_S8tHDEO*WDNixPw91l41im)XX!mfo>1mK0hE%Z9~LYG@HQogbl)UiuuwvGS;gFj zPm-BIKg0Vy9ACIDO-fJArnxPexHZfXxK-FaxyB36h3K?FRv_j0({D&9Z|mH=Rc z=cMkpTejD3TKdP=Y{qAo67sU%!q zLVXhO^3LKRCW$$fQ1UVWk-OH&rDEI_?uR?WWC#nCAjr=PGVd<6Ix~w-qZRdSCR8^Y zP*UHChIWs|JULzdA?}=`Mu!BXd{{6ap!)#?x-&8%z`ZeYJP;d<*l<4(7d%dFs}bj~ zw&8SfD@>L?A0S_F0JaaWk9+Ob*d;7yUkW$-y-R9k1vL`QwfNU3Pq#{Uyzt10mk)bl7D*A+6Dn{byV5l@E zG_>*e50pZ6@mg)(@%*xQt-+)KNHq$+yU^V(?k!9x87b7j=wZHCF((ExAGiHwpYRe9 zEd6~HzVxlfsUo#AabvYwlYOeFJi)@UQ+lp)xU;vA4FL>C#T?^$a2OUS!SXqG4Cu{w zgAm|}#Bg+)So~7%I1SJlO*u#NO2agI4iYd5F|Yq0WUEFP_z}*HJ<)B z>tb|8lX{{{dfYK$ltPBY(LpZv(-~Qmt8{49Szxwuko$TeYN!vwgIwE280znhQ6Z9| z*AAUk7|d2dk*Zq+EJz4*DVwTn(&M{JDwNdez)|}(Jyb5itO-LgbxaT#Cs9*LeWx}h z{dD`8l7?|Enr#EY1dLY5Fd|gSOP1p(zRGUEPep3<7s{wKCVZAvhqI+^cz$LSf@NNI zGR<1EW!rxXE8aSKB^E${FzatZun(9T8_Y|NqZGR{s&UH6&G$xClOAs#u7JA3+;h!n z;pi7d!k* ziLW(l1;;6UK@b5WLZpz0oll;|b`wq%vBaROlk@O<}= zpsg-%! zPd$PN2z20`Z$+cV1igv(MlIDCjoHU?OQSjrrs@3&GK9d-M+`ron>P60_}yW&K*TVR zdW#^Fbln;uK!9&oyVhW_(p@J68H#UL&FpClmHYOzi=O((F8&y>0G>DrktnDtujFdk#iEiS)Gr9| z9{g|`5J&)@Eh@RJPt}(C=9cq6W?hIkTP@Q702(zQ66HWXBpf2~@c#5(s%n8ec^|?8 zr6_%SVb@s6`v7#k0*x92S0 zZ~6h9-jMVC>2sm&+7A1;on;fy+N6iKBohI_lR${WT@{W}Xbl$l`P{Uj!)WPof>xG; z*1$n$vH}1ob^PvkLl6OtJa%juLNFmJ0O!lvz;ayW>72{0<=1PSrKwVwP1B)nY=G21 z7gE1O^s#ZaPL7ZUq238cUVI4q$??nQ&Roo& z%uzam_W+bCpwlX#?aR4w%_({2+9Sjn)JN_ z(>O{65q)<|$heA~PmlTPr75bg0EyE*Fd+~`fR&}Va*bb*vn6Q;9-HEN?h-L90Eag{ z0bhw2k57#aE#2{W@YYAiRd|URkDd|{oDF7`w(=W*5FAQfh?NWOVxy zp*OeI1Edo2+M@eM7XR~6=E;eQ5+OD_v`=Wqs|`AotN8f>5ks)!h1tk{FBx+ZMnWud zJnY!$VMu>tHK0IeMcoGYqDb(02qlLd-kKTI60Ky2htm-JYy9FpEST6Ex2qXG=W zZ-atj7Y;6Ysr#=W=VY<4ML(X)rgf_3IE8FJlK&X*QvYCoU(COMl#roqP}$Ii|6~@9 zgNbc}RC*dRc_LiACzpgdN*gZ~7FXn!l=Y;kD|Vg0l<|=+KRtJ0vaon|+hG{G0-hk~ zbq_#qs6>Fg6%yY`eD(140fw4}#>{=+9QDsFE}POjNAEP4@kr`dXx8xa2c7_>z~b$P zaiOH{mKJUVv~82ns>%Utso|r`h5(Ks)?3Hw=Hits1J`YNDx3>2FV?zKXw4(dBg|(Lakaa%);P)BR zI&_^8&J4SJwE@#seu|3vHeNR(1O}58i#C6aoriPyhAkUOo>`&TwnH-VT?qHem7g?@&AHy4~HHcmmX z0Y9d0vc2R8GdIoy$Y*f#hzvsr9-A7Al)3SkdG82_T=?>QrMwZJp2$P`u`AFS`Ry+C z0}jA8Oy$-=sqFuVOb`B`3aUty%+G zw0aa)HR00L2AsTd4P~_+%YAY{u^cB;&pT>#{zzsi?*V1f0Q9?Ac^jf0z*=Oi-w*U% zeZqfE&ffSixZlJE{COY1q`zSCtg48YDf3#1s>Xz0EVW4l3(k%yc&dTM&Ov8I5ddI_fLW^b2))2{{wp=5@|3jCCMo0QDlirHHt z!O^V%^?cD3C9809{q0#aWrv40&H!Q30P23(93gK)EKkF+Ww(FXoDGyp24|S30SGLd zBnQB-f5Qo-zzoPa2B&s--Wx$Mnb!b>Z|UsM5Mc$5r~bA}XYDX!^B9O|-^J4ewV+t% zEPVAO2A6 + + + + + + + + + + + + + + + + From 777fa068033613c3ea1b333947d0a4efd29fcf4d Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Mon, 9 Jun 2025 16:34:48 +0530 Subject: [PATCH 08/10] trying to attach --- environment-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment-dev.yml b/environment-dev.yml index 6d919c4f..af545d32 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -1,4 +1,4 @@ -name: xeus-cpp-2 +name: xeus-cpp channels: - conda-forge dependencies: From 10c8c06abe99dca3da906421129bbf161f206a9c Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Mon, 9 Jun 2025 16:40:36 +0530 Subject: [PATCH 09/10] trying to attach --- src/xdebugger.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 5c22211e..ac7c156d 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -371,19 +371,20 @@ namespace xcpp nl::json debugger::attach_request(const nl::json& message) { // Placeholder DAP response - std::cout << "debugger::attach_request (sending launch instead)" << std::endl; - nl::json launch_request = { + std::cout << "debugger::attach_request" << std::endl; + nl::json attach_request = { {"seq", 2}, {"type", "request"}, - {"command", "launch"}, - {"arguments", - {{"program", "/Users/abhinavkumar/Desktop/Coding/Testing/test"}, - {"args", {"arg1", "arg2"}}, - {"cwd", "/Users/abhinavkumar/Desktop/Coding/Testing"}, - {"initCommands", {"settings set plugin.jit-loader.gdb.enable on"}}}} + {"command", "attach"}, + {"arguments", { + {"pid", message["arguments"].value("pid", 0)}, + {"program", message["arguments"].value("program", "")}, + {"stopOnEntry", message["arguments"].value("stopOnEntry", false)}, + {"initCommands", message["arguments"].value("initCommands", nl::json::array())} + }} }; - nl::json reply = forward_message(launch_request); - std::cout << "Launch request sent instead of attach: " << reply.dump() << std::endl; + nl::json reply = forward_message(attach_request); + std::cout << "Attach request sent: " << reply.dump() << std::endl; return reply; } From 8df9839148fc5414b94f765ac56372bc69b42f56 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 12 Jun 2025 13:47:06 +0530 Subject: [PATCH 10/10] moved cppinterop out-of-process --- CMakeLists.txt | 61 ++++ include/xeus-cpp/xinterpreter.hpp | 5 + include/xeus-cpp/xshared_memory.hpp | 147 +++++++++ src/main.cpp | 12 + src/xcppinterop_client.hpp | 317 +++++++++++++++++++ src/xcppinterop_process.cpp | 462 ++++++++++++++++++++++++++++ src/xinterpreter.cpp | 60 ++-- 7 files changed, 1022 insertions(+), 42 deletions(-) create mode 100644 include/xeus-cpp/xshared_memory.hpp create mode 100644 src/xcppinterop_client.hpp create mode 100644 src/xcppinterop_process.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eb84445b..64710e14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,7 @@ set(XEUS_CPP_HEADERS include/xeus-cpp/xoptions.hpp include/xeus-cpp/xpreamble.hpp include/xeus-cpp/xdebugger.hpp + include/xeus-cpp/xshared_memory.hpp #src/xinspect.hpp #src/xsystem.hpp #src/xparser.hpp @@ -212,6 +213,7 @@ set(XEUS_CPP_SRC src/xdebugger.cpp src/xinternal_utils.cpp src/xdebuglldb_client.cpp + src/xcppinterop_process.cpp ) if(NOT EMSCRIPTEN) @@ -510,6 +512,65 @@ if(XEUS_CPP_BUILD_EXECUTABLE OR EMSCRIPTEN) endif () endif () +# cppinterop_process executable +# ============================= + +if (XEUS_CPP_BUILD_EXECUTABLE AND NOT EMSCRIPTEN) + # Define source files for cppinterop_process + set(CPPINTEROP_PROCESS_SRC + src/xcppinterop_process.cpp # You'll need to create this file + # Add any other source files needed for the process + ${XEUS_CPP_HEADERS} + ) + + add_executable(cppinterop_process ${CPPINTEROP_PROCESS_SRC}) + + # Add xeus-cpp include directory + target_include_directories(cppinterop_process + PRIVATE + ${XEUS_CPP_INCLUDE_DIR} + ) + + # Set common compile options + xeus_cpp_set_common_options(cppinterop_process) + + target_compile_options(cppinterop_process PRIVATE + -gdwarf-4 # Generate DWARF-4 debug information + -O0 # Disable optimization + ) + + # Link with necessary libraries + target_link_libraries(cppinterop_process PRIVATE + clangCppInterOp + ${CMAKE_THREAD_LIBS_INIT} + ) + + # Add any additional libraries needed for shared memory, etc. + if(CMAKE_DL_LIBS) + target_link_libraries(cppinterop_process PRIVATE ${CMAKE_DL_LIBS} util) + endif() + + # Set target properties + set_target_properties(cppinterop_process PROPERTIES + INSTALL_RPATH_USE_LINK_PATH TRUE + ) + + if (APPLE) + set_target_properties(cppinterop_process PROPERTIES + MACOSX_RPATH ON + ) + else() + set_target_properties(cppinterop_process PROPERTIES + BUILD_WITH_INSTALL_RPATH 1 + SKIP_BUILD_RPATH FALSE + ) + endif() + + # Install the executable + install(TARGETS cppinterop_process + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + # Configure 'xeus-cppConfig.cmake' for a build tree set(XEUS_CPP_CONFIG_CODE "####### Expanded from \@XEUS_CPP_CONFIG_CODE\@ #######\n") set(XEUS_CPP_CONFIG_CODE "${XEUS_CPP_CONFIG_CODE}set(CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake;\${CMAKE_MODULE_PATH}\")\n") diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index 40750e86..ef76a2ad 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -30,6 +30,7 @@ namespace nl = nlohmann; namespace xcpp { + class CppInterOpClient; class XEUS_CPP_API interpreter : public xeus::xinterpreter { public: @@ -37,6 +38,8 @@ namespace xcpp interpreter(int argc, const char* const* argv); virtual ~interpreter(); + void set_cppinterop_client(std::shared_ptr client); + void publish_stdout(const std::string&); void publish_stderr(const std::string&); @@ -87,6 +90,8 @@ namespace xcpp xoutput_buffer m_cout_buffer; xoutput_buffer m_cerr_buffer; + + std::shared_ptr m_cppinterop_client; }; } diff --git a/include/xeus-cpp/xshared_memory.hpp b/include/xeus-cpp/xshared_memory.hpp new file mode 100644 index 00000000..de4a13df --- /dev/null +++ b/include/xeus-cpp/xshared_memory.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct SharedMemoryBuffer { + // FIXED: Reduced buffer sizes to fit within 64KB system limit + // Total struct size should be around 52KB, leaving room for other fields + static constexpr size_t MAX_CODE_SIZE = 16 * 1024; // 16KB for code + static constexpr size_t MAX_OUTPUT_SIZE = 16 * 1024; // 16KB for output + static constexpr size_t MAX_ERROR_SIZE = 8 * 1024; // 8KB for errors + static constexpr size_t MAX_COMPLETION_SIZE = 8 * 1024; // 8KB for completions + + enum class RequestType : uint32_t { + NONE = 0, + PROCESS_CODE, + CODE_COMPLETE, + EVALUATE, + SHUTDOWN + }; + + enum class ResponseStatus : uint32_t { + NONE = 0, + SUCCESS, + COMPILATION_ERROR, + RUNTIME_ERROR, + SYSTEM_ERROR + }; + + std::atomic request_ready{false}; + std::atomic response_ready{false}; + std::atomic request_type{RequestType::NONE}; + std::atomic response_status{ResponseStatus::NONE}; + + char code_buffer[MAX_CODE_SIZE]; + uint32_t code_length; + int cursor_pos; + + char output_buffer[MAX_OUTPUT_SIZE]; + char error_buffer[MAX_ERROR_SIZE]; + uint32_t output_length; + uint32_t error_length; + bool compilation_result; + int64_t evaluation_result; + + char completion_buffer[MAX_COMPLETION_SIZE]; // Use separate size constant + uint32_t completion_length; + + void reset() { + request_ready.store(false, std::memory_order_relaxed); + response_ready.store(false, std::memory_order_relaxed); + request_type.store(RequestType::NONE, std::memory_order_relaxed); + response_status.store(ResponseStatus::NONE, std::memory_order_relaxed); + code_length = 0; + output_length = 0; + error_length = 0; + completion_length = 0; + cursor_pos = 0; + compilation_result = false; + evaluation_result = 0; + + memset(code_buffer, 0, MAX_CODE_SIZE); + memset(output_buffer, 0, MAX_OUTPUT_SIZE); + memset(error_buffer, 0, MAX_ERROR_SIZE); + memset(completion_buffer, 0, MAX_COMPLETION_SIZE); + } + + void set_code(const std::string& code) { + code_length = std::min(code.length(), MAX_CODE_SIZE - 1); + memcpy(code_buffer, code.c_str(), code_length); + code_buffer[code_length] = '\0'; + } + + std::string get_code() const { + return std::string(code_buffer, code_length); + } + + void set_output(const std::string& output) { + output_length = std::min(output.length(), MAX_OUTPUT_SIZE - 1); + memcpy(output_buffer, output.c_str(), output_length); + output_buffer[output_length] = '\0'; + } + + std::string get_output() const { + return std::string(output_buffer, output_length); + } + + void set_error(const std::string& error) { + error_length = std::min(error.length(), MAX_ERROR_SIZE - 1); + memcpy(error_buffer, error.c_str(), error_length); + error_buffer[error_length] = '\0'; + } + + std::string get_error() const { + return std::string(error_buffer, error_length); + } + + void set_completions(const std::vector& completions) { + std::string combined; + for (const auto& comp : completions) { + if (!combined.empty()) combined += "\n"; + combined += comp; + } + completion_length = std::min(combined.length(), MAX_COMPLETION_SIZE - 1); + memcpy(completion_buffer, combined.c_str(), completion_length); + completion_buffer[completion_length] = '\0'; + } + + std::vector get_completions() const { + std::vector result; + std::string data(completion_buffer, completion_length); + std::istringstream iss(data); + std::string line; + while (std::getline(iss, line)) { + result.push_back(line); + } + return result; + } + + // Helper function to get the total size of this struct + static constexpr size_t total_size() { + return sizeof(SharedMemoryBuffer); + } + + // Helper function to check if a given size can accommodate this struct + static bool fits_in_size(size_t available_size) { + return available_size >= sizeof(SharedMemoryBuffer); + } +}; + +// Static assertion to ensure the struct fits in reasonable shared memory limits +static_assert(sizeof(SharedMemoryBuffer) <= 65536, + "SharedMemoryBuffer too large for typical shared memory limits"); + +// Print size information for debugging +inline void print_buffer_size_info() { + std::cout << "SharedMemoryBuffer size breakdown:" << std::endl; + std::cout << " Code buffer: " << SharedMemoryBuffer::MAX_CODE_SIZE << " bytes" << std::endl; + std::cout << " Output buffer: " << SharedMemoryBuffer::MAX_OUTPUT_SIZE << " bytes" << std::endl; + std::cout << " Error buffer: " << SharedMemoryBuffer::MAX_ERROR_SIZE << " bytes" << std::endl; + std::cout << " Completion buffer: " << SharedMemoryBuffer::MAX_COMPLETION_SIZE << " bytes" << std::endl; + std::cout << " Total struct size: " << sizeof(SharedMemoryBuffer) << " bytes" << std::endl; + std::cout << " Fits in 64KB: " << (sizeof(SharedMemoryBuffer) <= 65536 ? "YES" : "NO") << std::endl; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index dffb2216..9277ef16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,6 +30,8 @@ #include "xeus-cpp/xutils.hpp" #include "xeus-cpp/xdebugger.hpp" +#include "xcppinterop_client.hpp" + namespace nl = nlohmann; int main(int argc, char* argv[]) @@ -70,6 +72,16 @@ int main(int argc, char* argv[]) auto interpreter = std::make_unique(argc, argv); std::unique_ptr context = xeus::make_zmq_context(); + auto cppinterop_client = std::make_shared(); + if (!cppinterop_client->initialize()) + { + std::cerr << "Failed to initialize CppInterOpClient" << std::endl; + return 1; + } + + interpreter->set_cppinterop_client(cppinterop_client); + + if (!file_name.empty()) { xeus::xconfiguration config = xeus::load_configuration(file_name); diff --git a/src/xcppinterop_client.hpp b/src/xcppinterop_client.hpp new file mode 100644 index 00000000..158bbbe6 --- /dev/null +++ b/src/xcppinterop_client.hpp @@ -0,0 +1,317 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using Args = std::vector; + + +#include "xeus-cpp/xshared_memory.hpp" + +namespace xcpp +{ + class CppInterOpClient + { + private: + + SharedMemoryBuffer* m_shared_buffer; + int m_shm_fd; + std::string m_shm_name; + pid_t m_child_pid; + bool m_initialized; + + public: + + CppInterOpClient() + : m_shared_buffer(nullptr) + , m_shm_fd(-1) + , m_child_pid(-1) + , m_initialized(false) + { + // Generate unique shared memory name + m_shm_name = "/xcpp_shm_" + std::to_string(getpid()); + } + + ~CppInterOpClient() + { + std::cerr << "[~CppInterOpClient] Cleaning up...\n"; + cleanup(); + } + + bool initialize() + { + // Create shared memory + shm_unlink(m_shm_name.c_str()); + m_shm_fd = shm_open(m_shm_name.c_str(), O_CREAT | O_RDWR | O_EXCL, 0666); + if (m_shm_fd == -1) + { + std::cerr << "Failed to create shared memory" << std::endl; + return false; + } + + // Set size + if (ftruncate(m_shm_fd, sizeof(SharedMemoryBuffer)) == -1) + { + std::cerr << "Failed to set shared memory size" << std::endl; + return false; + } + + // Map shared memory + m_shared_buffer = static_cast( + mmap(nullptr, sizeof(SharedMemoryBuffer), PROT_READ | PROT_WRITE, MAP_SHARED, m_shm_fd, 0) + ); + + if (m_shared_buffer == MAP_FAILED) + { + std::cerr << "Failed to map shared memory" << std::endl; + return false; + } + + // Initialize shared buffer + m_shared_buffer->reset(); + + // Fork and exec the CppInterOp process, redirecting child stdout/stderr to parent's + int pipefd[2]; + if (pipe(pipefd) == -1) + { + std::cerr << "Failed to create pipe for child logs" << std::endl; + return false; + } + + // ******************** + // This code block can be simplified more. Currently, it also fetches the logs of the child process. + m_child_pid = fork(); + if (m_child_pid == -1) + { + std::cerr << "Failed to fork CppInterOp process" << std::endl; + close(pipefd[0]); + close(pipefd[1]); + return false; + } + + if (m_child_pid == 0) + { + pid_t parent_pid = getppid(); + // Start monitoring thread + std::thread([]() { + while (true) { + if (getppid() == 1) { // Parent died, we're now child of init + exit(1); + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + }).detach(); + + close(pipefd[0]); + dup2(pipefd[1], STDOUT_FILENO); + dup2(pipefd[1], STDERR_FILENO); + close(pipefd[1]); + + execl("./cppinterop_process", "cppinterop_process", m_shm_name.c_str(), nullptr); + std::cerr << "Failed to exec CppInterOp process" << std::endl; + exit(1); + } + else + { + close(pipefd[1]); + std::thread([read_fd = pipefd[0]]() { + char buffer[256]; + ssize_t n; + while ((n = read(read_fd, buffer, sizeof(buffer) - 1)) > 0) + { + buffer[n] = '\0'; + // std::cout << "[CppInterOp child] " << buffer; + } + close(read_fd); + }).detach(); + } + // ******************** + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + int status; + pid_t result = waitpid(m_child_pid, &status, WNOHANG); + if (result != 0) + { + std::cerr << "CppInterOp process failed to start" << std::endl; + return false; + } + + m_initialized = true; + std::clog << "CppInterOp client initialized with child PID: " << m_child_pid << std::endl; + return true; + } + + bool processCode(const std::string& code, std::string& output, std::string& error) + { + if (!m_initialized) + { + return false; + } + + m_shared_buffer->reset(); + m_shared_buffer->set_code(code); + m_shared_buffer->request_type = SharedMemoryBuffer::RequestType::PROCESS_CODE; + m_shared_buffer->request_ready.store(true, std::memory_order_release); + + // Wait for response + if (!waitForResponse()) + { + return false; + } + + output = m_shared_buffer->get_output(); + error = m_shared_buffer->get_error(); + + return m_shared_buffer->response_status.load() == SharedMemoryBuffer::ResponseStatus::SUCCESS; + } + + bool codeComplete(const std::string& code, int cursor_pos, std::vector& results) + { + if (!m_initialized) + { + return false; + } + + m_shared_buffer->reset(); + m_shared_buffer->set_code(code); + m_shared_buffer->cursor_pos = cursor_pos; + m_shared_buffer->request_type = SharedMemoryBuffer::RequestType::CODE_COMPLETE; + m_shared_buffer->request_ready = true; + + // Wait for response + if (!waitForResponse()) + { + return false; + } + + results = m_shared_buffer->get_completions(); + return m_shared_buffer->response_status.load() == SharedMemoryBuffer::ResponseStatus::SUCCESS; + } + + bool evaluate(const std::string& code, int64_t& result) + { + if (!m_initialized) + { + return false; + } + + m_shared_buffer->reset(); + m_shared_buffer->set_code(code); + m_shared_buffer->request_type = SharedMemoryBuffer::RequestType::EVALUATE; + m_shared_buffer->request_ready = true; + + // Wait for response + if (!waitForResponse()) + { + return false; + } + + if (m_shared_buffer->response_status.load() == SharedMemoryBuffer::ResponseStatus::SUCCESS) + { + result = m_shared_buffer->evaluation_result; + return true; + } + + return false; + } + + void shutdown() + { + if (!m_initialized) + { + return; + } + + m_shared_buffer->request_type = SharedMemoryBuffer::RequestType::SHUTDOWN; + m_shared_buffer->request_ready = true; + + // Wait a bit for graceful shutdown + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + // Kill child process if it's still running + if (m_child_pid > 0) + { + std::cout << "Terminating CppInterOp child process with PID: " << m_child_pid << std::endl; + kill(m_child_pid, SIGTERM); + + // Wait for child to exit + int status; + waitpid(m_child_pid, &status, 0); + m_child_pid = -1; + } + } + + void cleanup() + { + if (m_child_pid > 0) { + kill(-m_child_pid, SIGKILL); // Negative PID kills process group + } + + if (m_initialized) + { + shutdown(); + } + + if (m_shared_buffer && m_shared_buffer != MAP_FAILED) + { + munmap(m_shared_buffer, sizeof(SharedMemoryBuffer)); + m_shared_buffer = nullptr; + } + + if (m_shm_fd != -1) + { + close(m_shm_fd); + shm_unlink(m_shm_name.c_str()); + m_shm_fd = -1; + } + } + + private: + + bool waitForResponse(int timeout_ms = 100000) + { + auto start = std::chrono::steady_clock::now(); + + while (!m_shared_buffer->response_ready.load()) + { + auto now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - start).count() > timeout_ms) + { + std::cerr << "Timeout waiting for CppInterOp response" << std::endl; + return false; + } + + // Check if child process is still alive + int status; + pid_t result = waitpid(m_child_pid, &status, WNOHANG); + static int elapsed_ms = 0; + if (result != 0) + { + elapsed_ms += 1; + } + else + { + elapsed_ms = 0; + } + // If more than 100 seconds have passed, return false + if (elapsed_ms > 100000) + { + std::cerr << "Timeout: No response from CppInterOp process after 10 seconds" << std::endl; + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + elapsed_ms += 1; + } + + return true; + } + }; +}; + diff --git a/src/xcppinterop_process.cpp b/src/xcppinterop_process.cpp new file mode 100644 index 00000000..1524483e --- /dev/null +++ b/src/xcppinterop_process.cpp @@ -0,0 +1,462 @@ +// cppinterop_process.cpp +// Separate process that handles CppInterOp operations - FIXED VERSION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "clang/Interpreter/CppInterOp.h" // from CppInterOp package +#include "xeus-cpp/xshared_memory.hpp" + +class CppInterOpProcess { +private: + void* m_interpreter; + SharedMemoryBuffer* m_shared_buffer; + int m_shm_fd; + bool m_running; + std::string m_shm_name; + size_t m_shm_size; + static std::atomic initialized; + static std::mutex init_mutex; + + // Helper function to get system shared memory limits + size_t getMaxShmSize() { + // Try to read system limits + std::ifstream shmmax("/proc/sys/kernel/shmmax"); + if (shmmax.is_open()) { + size_t max_size; + shmmax >> max_size; + shmmax.close(); + return max_size; + } + + // Fallback to a conservative size (1MB for better compatibility) + return 1024 * 1024; + } + + // Helper function to validate and adjust shared memory size + size_t validateShmSize(size_t requested_size) { + size_t max_size = getMaxShmSize(); + size_t min_size = sizeof(SharedMemoryBuffer); + + std::clog << "Requested SHM size: " << requested_size << " bytes" << std::endl; + std::clog << "System max SHM size: " << max_size << " bytes" << std::endl; + std::clog << "Minimum required size: " << min_size << " bytes" << std::endl; + + if (requested_size > max_size) { + std::clog << "Warning: Requested size exceeds system limit, using " << max_size << " bytes" << std::endl; + return max_size; + } + + if (requested_size < min_size) { + std::clog << "Warning: Requested size too small, using minimum " << min_size << " bytes" << std::endl; + return min_size; + } + + return requested_size; + } + + // Fix corrupted include paths + std::vector sanitizeIncludePaths(const std::vector& paths) { + std::vector sanitized; + + for (size_t i = 0; i < paths.size(); ++i) { + const std::string& path = paths[i]; + + std::clog << "Processing path[" << i << "]: '" << path << "' (length: " << path.length() << ")" << std::endl; + + // Skip empty or obviously corrupted paths + if (path.empty() || path.length() < 3) { + std::clog << "Skipping invalid path (too short): '" << path << "'" << std::endl; + continue; + } + + // Check if path contains non-printable characters or null bytes + bool has_invalid_chars = false; + for (size_t j = 0; j < path.length(); ++j) { + char c = path[j]; + if (c == '\0' || (c > 0 && c < 32 && c != '\n' && c != '\t')) { + has_invalid_chars = true; + std::clog << "Found invalid character at position " << j << ": 0x" << std::hex << (int)(unsigned char)c << std::dec << std::endl; + break; + } + } + + if (has_invalid_chars) { + std::clog << "Skipping path with invalid characters: '" << path << "'" << std::endl; + continue; + } + + // Additional check: path should start with '/' on Unix systems + if (path[0] != '/') { + std::clog << "Skipping relative path: '" << path << "'" << std::endl; + continue; + } + + // Check if directory actually exists + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { + sanitized.push_back(path); + std::clog << "Added valid include path: " << path << std::endl; + } else { + std::clog << "Skipping non-existent path: " << path << " (error: " << strerror(errno) << ")" << std::endl; + } + } + + return sanitized; + } + + // Alternative: try to use minimal system includes if detection fails + std::vector getMinimalSystemIncludes() { + std::vector minimal_includes; + + // Common macOS system include paths + std::vector candidates = { + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1", + "/Library/Developer/CommandLineTools/usr/lib/clang/17/include", + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include", + "/usr/include", + "/usr/local/include" + }; + + for (const std::string& path : candidates) { + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) { + minimal_includes.push_back(path); + std::clog << "Added minimal system include: " << path << std::endl; + } + } + + return minimal_includes; + } + +public: + CppInterOpProcess(const std::string& shm_name, size_t shm_size = sizeof(SharedMemoryBuffer)) + : m_interpreter(nullptr), m_shared_buffer(nullptr), + m_shm_fd(-1), m_running(true), m_shm_name(shm_name) { + m_shm_size = validateShmSize(shm_size); + } + + ~CppInterOpProcess() { + cleanup(); + } + + bool initialize() { + // Create shared memory + m_shm_fd = -1; + for (int i = 0; i < 50; ++i) { + m_shm_fd = shm_open(m_shm_name.c_str(), O_RDWR, 0666); + if (m_shm_fd != -1) break; + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + if(m_shm_fd == -1) { + std::cerr << "Failed to open shared memory: " << strerror(errno) << std::endl; + return false; + } + + // Map shared memory + m_shared_buffer = static_cast( + mmap(nullptr, m_shm_size, + PROT_READ | PROT_WRITE, MAP_SHARED, m_shm_fd, 0)); + + if (m_shared_buffer == MAP_FAILED) { + std::cerr << "Failed to map shared memory: " << strerror(errno) << std::endl; + return false; + } + + std::clog << "Successfully mapped shared memory at " << m_shared_buffer << std::endl; + + // Initialize shared buffer + m_shared_buffer->reset(); + + // Initialize CppInterOp interpreter + if (!initializeInterpreter()) { + std::cerr << "Failed to initialize CppInterOp interpreter" << std::endl; + cleanup(); + return false; + } + + std::clog << "CppInterOp process initialized successfully at shm_name: " << m_shm_name << std::endl; + initialized.store(true); + return true; + } + + void run() { + std::clog << "CppInterOp process started, waiting for requests..." << std::endl; + + while (m_running) { + // Check for new requests + // std::clog << m_shared_buffer->request_ready.load() << std::endl; + if (m_shared_buffer->request_ready.load(std::memory_order_acquire)) { + processRequest(); + m_shared_buffer->request_ready.store(false); + m_shared_buffer->response_ready.store(true); + } + + // Small delay to prevent busy waiting + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + + std::clog << "CppInterOp process shutting down..." << std::endl; + } + + // Get current shared memory size + size_t getSharedMemorySize() const { + return m_shm_size; + } + +private: +bool initializeInterpreter() { + try { + // Simplified ClangArgs construction + std::vector ClangArgs; + std::vector args_storage; + + ClangArgs.push_back("-g"); + ClangArgs.push_back("-O0"); + + // Add resource directory + std::string resource_dir = Cpp::DetectResourceDir(); + if (!resource_dir.empty()) { + ClangArgs.push_back("-resource-dir"); + ClangArgs.push_back(resource_dir.c_str()); + std::clog << "Using resource directory: " << resource_dir << std::endl; + } else { + std::clog << "Failed to detect resource-dir" << std::endl; + } + + // Detect and sanitize system includes + std::vector CxxSystemIncludes; + Cpp::DetectSystemCompilerIncludePaths(CxxSystemIncludes); + std::clog << "Detected " << CxxSystemIncludes.size() << " system include paths (before sanitization)" << std::endl; + + CxxSystemIncludes = sanitizeIncludePaths(CxxSystemIncludes); + std::clog << "Using " << CxxSystemIncludes.size() << " valid system include paths (after sanitization)" << std::endl; + + // Add -isystem for each include path + for (const std::string& include : CxxSystemIncludes) { + ClangArgs.push_back("-isystem"); + ClangArgs.push_back(include.c_str()); + std::clog << "Added: -isystem " << include << std::endl; + } + + for (size_t i = 0; i < ClangArgs.size(); ++i) { + std::clog << " Arg[" << i << "]: '" << (ClangArgs[i] ? ClangArgs[i] : "") << "'" << std::endl; + } + + m_interpreter = Cpp::CreateInterpreter(ClangArgs); + + if (m_interpreter) { + std::clog << "CppInterOp interpreter created successfully" << std::endl; + return true; + } else { + std::cerr << "CppInterOp interpreter creation returned null" << std::endl; + return false; + } + } catch (const std::exception& e) { + std::cerr << "Exception during interpreter initialization: " << e.what() << std::endl; + return false; + } + } + + void processRequest() { + auto request_type = m_shared_buffer->request_type.load(); + std::clog << "Processing request type: " << static_cast(request_type) << std::endl; + + try { + switch (request_type) { + case SharedMemoryBuffer::RequestType::PROCESS_CODE: + processCode(); + break; + + case SharedMemoryBuffer::RequestType::CODE_COMPLETE: + processCodeCompletion(); + break; + + case SharedMemoryBuffer::RequestType::EVALUATE: + processEvaluation(); + break; + + case SharedMemoryBuffer::RequestType::SHUTDOWN: + m_running = false; + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SUCCESS; + break; + + default: + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error("Unknown request type"); + break; + } + } + catch (const std::exception& e) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error(std::string("Exception: ") + e.what()); + } + } + + void processCode() { + std::string code = m_shared_buffer->get_code(); + + std::clog << "Processing code in CppInterOpProcess: " << code << std::endl; + + if (!m_interpreter) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error("Interpreter not initialized"); + return; + } + + try { + // Capture streams similar to your original StreamRedirectRAII + Cpp::BeginStdStreamCapture(Cpp::kStdErr); // stderr + Cpp::BeginStdStreamCapture(Cpp::kStdOut); // stdout + + bool compilation_result = Cpp::Process(code.c_str()); + + std::string output = Cpp::EndStdStreamCapture(); // stdout + std::string error = Cpp::EndStdStreamCapture(); // stderr + + m_shared_buffer->compilation_result = compilation_result; + m_shared_buffer->set_output(output); + m_shared_buffer->set_error(error); + + if (!compilation_result) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::COMPILATION_ERROR; + } else { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SUCCESS; + } + } catch (const std::exception& e) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error(std::string("Code processing exception: ") + e.what()); + } + } + + void processCodeCompletion() { + if (!m_interpreter) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error("Interpreter not initialized"); + return; + } + + try { + std::string code = m_shared_buffer->get_code(); + int cursor_pos = m_shared_buffer->cursor_pos; + + std::vector results; + Cpp::CodeComplete(results, code.c_str(), 1, cursor_pos + 1); + + m_shared_buffer->set_completions(results); + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SUCCESS; + } catch (const std::exception& e) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error(std::string("Code completion exception: ") + e.what()); + } + } + + void processEvaluation() { + if (!m_interpreter) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SYSTEM_ERROR; + m_shared_buffer->set_error("Interpreter not initialized"); + return; + } + + try { + std::string code = m_shared_buffer->get_code(); + + int64_t result = Cpp::Evaluate(code.c_str()); + m_shared_buffer->evaluation_result = result; + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::SUCCESS; + } + catch (const std::exception& e) { + m_shared_buffer->response_status = SharedMemoryBuffer::ResponseStatus::RUNTIME_ERROR; + m_shared_buffer->set_error(std::string("Evaluation exception: ") + e.what()); + } + } + + void cleanup() { + std::lock_guard lock(init_mutex); + initialized.store(false); + + if (m_interpreter) { + // Note: CppInterOp might not have explicit cleanup, + // but setting to nullptr is safer + m_interpreter = nullptr; + } + + if (m_shared_buffer && m_shared_buffer != MAP_FAILED) { + munmap(m_shared_buffer, m_shm_size); + m_shared_buffer = nullptr; + } + + if (m_shm_fd != -1) { + close(m_shm_fd); + shm_unlink(m_shm_name.c_str()); + m_shm_fd = -1; + } + } +}; + +// Signal handler for graceful shutdown +std::atomic CppInterOpProcess::initialized{false}; +std::mutex CppInterOpProcess::init_mutex; +static CppInterOpProcess* g_process = nullptr; + +void signal_handler(int sig) { + if (g_process) { + std::clog << "Received signal " << sig << ", shutting down..." << std::endl; + // The process will exit on next iteration + } +} + +int main(int argc, char* argv[]) { + if (argc < 2 || argc > 3) { + std::cerr << "Usage: " << argv[0] << " [shared_memory_size]" << std::endl; + return 1; + } + + std::string shm_name = argv[1]; + size_t shm_size = sizeof(SharedMemoryBuffer); + + if (argc == 3) { + try { + shm_size = std::stoull(argv[2]); + } catch (const std::exception& e) { + std::cerr << "Invalid shared memory size: " << argv[2] << std::endl; + return 1; + } + } + + // Setup signal handlers + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + CppInterOpProcess process(shm_name, shm_size); + g_process = &process; + + std::clog << "Initializing CppInterOp process with shared memory '" + << shm_name << "' (size: " << process.getSharedMemorySize() << " bytes)" << std::endl; + + if (!process.initialize()) { + std::cerr << "Failed to initialize CppInterOp process" << std::endl; + return 1; + } + + process.run(); + + g_process = nullptr; + return 0; +} \ No newline at end of file diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index d5966b13..a9d9fbe6 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -27,46 +27,14 @@ using Args = std::vector; -void* createInterpreter(const Args &ExtraArgs = {}) { - Args ClangArgs = {/*"-xc++"*/"-v"}; - if (std::find_if(ExtraArgs.begin(), ExtraArgs.end(), [](const std::string& s) { - return s == "-resource-dir";}) == ExtraArgs.end()) { - std::string resource_dir = Cpp::DetectResourceDir(); - if (!resource_dir.empty()) { - ClangArgs.push_back("-resource-dir"); - ClangArgs.push_back(resource_dir.c_str()); - } else { - std::cerr << "Failed to detect the resource-dir\n"; - } - } - std::vector CxxSystemIncludes; - Cpp::DetectSystemCompilerIncludePaths(CxxSystemIncludes); - for (const std::string& CxxInclude : CxxSystemIncludes) { - ClangArgs.push_back("-isystem"); - ClangArgs.push_back(CxxInclude.c_str()); - } - ClangArgs.insert(ClangArgs.end(), ExtraArgs.begin(), ExtraArgs.end()); - // FIXME: We should process the kernel input options and conditionally pass - // the gpu args here. - return Cpp::CreateInterpreter(ClangArgs/*, {"-cuda"}*/); -} + +#include "xeus-cpp/xshared_memory.hpp" +#include "xcppinterop_client.hpp" using namespace std::placeholders; namespace xcpp { - struct StreamRedirectRAII { - std::string &err; - StreamRedirectRAII(std::string &e) : err(e) { - Cpp::BeginStdStreamCapture(Cpp::kStdErr); - Cpp::BeginStdStreamCapture(Cpp::kStdOut); - } - ~StreamRedirectRAII() { - std::string out = Cpp::EndStdStreamCapture(); - err = Cpp::EndStdStreamCapture(); - std::cout << out; - } - }; void interpreter::configure_impl() { @@ -96,8 +64,8 @@ int __get_cxx_version () { } __get_cxx_version () )"; - auto cxx_version = Cpp::Evaluate(code); - return std::to_string(cxx_version); + + return "17"; } interpreter::interpreter(int argc, const char* const* argv) : @@ -108,8 +76,6 @@ __get_cxx_version () , m_cerr_buffer(std::bind(&interpreter::publish_stderr, this, _1)) { //NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) - createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); - m_version = get_stdopt(); redirect_output(); init_preamble(); init_magic(); @@ -120,6 +86,12 @@ __get_cxx_version () restore_output(); } + void interpreter::set_cppinterop_client(std::shared_ptr client) + { + m_cppinterop_client = std::move(client); + m_version = get_stdopt(); + } + void interpreter::execute_request_impl( send_reply_callback cb, int /*execution_count*/, @@ -162,13 +134,17 @@ __get_cxx_version () std::cerr.rdbuf(&null); } - std::string err; + std::string output, err; // Attempt normal evaluation try { - StreamRedirectRAII R(err); - compilation_result = Cpp::Process(code.c_str()); + compilation_result = m_cppinterop_client->processCode(code, output, err); + + // Print output to stdout + if (!output.empty()) { + std::cout << output; + } } catch (std::exception& e) {