From 889f32f4576345662421b08f41026e0eb8343f2a Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Tue, 6 May 2025 20:26:38 +0530 Subject: [PATCH 01/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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 a6a02dcffc0a2f1d625a44a7520e3fcb2005700f Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 19 Jun 2025 13:20:54 +0530 Subject: [PATCH 10/19] out-of-process JIT execution --- .vscode/settings.json | 51 +++++++++++- include/xeus-cpp/xdebugger.hpp | 1 + include/xeus-cpp/xinterpreter.hpp | 2 + share/jupyter/kernels/xcpp17/kernel.json.in | 2 +- src/xdebugger.cpp | 90 ++++----------------- src/xinterpreter.cpp | 53 ++++++++++-- 6 files changed, 116 insertions(+), 83 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0cba2e68..370a27a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,54 @@ { "files.associations": { - "iostream": "cpp" + "iostream": "cpp", + "fstream": "cpp", + "ios": "cpp", + "locale": "cpp", + "sstream": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "array": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "map": "cpp", + "mutex": "cpp", + "new": "cpp", + "print": "cpp", + "regex": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "cstdarg": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "execution": "cpp", + "iomanip": "cpp", + "optional": "cpp", + "queue": "cpp", + "ratio": "cpp", + "stack": "cpp", + "variant": "cpp" } } \ No newline at end of file diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp index 7fd8311f..40874bb7 100644 --- a/include/xeus-cpp/xdebugger.hpp +++ b/include/xeus-cpp/xdebugger.hpp @@ -69,6 +69,7 @@ namespace xcpp bool m_is_running; int m_tcp_socket; bool m_tcp_connected; + std::string jit_process_pid; }; XEUS_CPP_API diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index 40750e86..0d2332b8 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -40,6 +40,8 @@ namespace xcpp void publish_stdout(const std::string&); void publish_stderr(const std::string&); + static std::string get_current_pid(); + private: void configure_impl() override; diff --git a/share/jupyter/kernels/xcpp17/kernel.json.in b/share/jupyter/kernels/xcpp17/kernel.json.in index 10b5b5d6..c09f6836 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": true + "metadata": {"debugger": false } } diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index ac7c156d..27bbebbf 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -1,13 +1,13 @@ #include "xeus-cpp/xdebugger.hpp" -#include // For inet_pton(), htons() -#include // For std::chrono (used in sleep_for) +#include +#include #include #include #include #include -#include // For sockaddr_in, AF_INET -#include // For socket(), connect(), send(), recv() +#include +#include #include #include #include @@ -15,6 +15,7 @@ #include "nlohmann/json.hpp" #include "xdebuglldb_client.hpp" +#include "xeus-cpp/xinterpreter.hpp" #include "xeus-zmq/xmiddleware.hpp" #include "xeus/xsystem.hpp" #include "xinternal_utils.hpp" @@ -56,8 +57,6 @@ namespace xcpp std::bind(&debugger::configuration_done_request, this, _1), true ); - - std::cout << "Debugger initialized with config: " << m_debugger_config.dump() << std::endl; } debugger::~debugger() @@ -70,6 +69,7 @@ namespace xcpp bool debugger::start_lldb() { std::cout << "debugger::start_lldb" << std::endl; + jit_process_pid = xcpp::interpreter::get_current_pid(); // Find a free port for LLDB-DAP m_lldb_port = xeus::find_free_port(100, 9999, 10099); @@ -263,23 +263,6 @@ namespace xcpp ); client.detach(); - // Now test direct TCP communication - nl::json init_request = { - {"seq", 1}, - {"type", "request"}, - {"command", "initialize"}, - {"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"); @@ -295,42 +278,8 @@ namespace xcpp // Dummy implementations for other methods 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 << "[debugger::inspect_variables_request] inspect_variables_request not implemented" + << std::endl; std::cout << message.dump() << std::endl; nl::json reply = { {"type", "response"}, @@ -339,16 +288,12 @@ namespace xcpp {"command", message["command"]}, {"body", {{"variables", - {{{"name", "a"}, - {"value", "100"}, - {"type", "int"}, - {"evaluateName", "a"}, - {"variablesReference", 0}}, - {{"name", "b"}, - {"value", "1000"}, - {"type", "int"}, - {"evaluateName", "b"}, - {"variablesReference", 0}}}}}} + {{{"name", "a"}, {"value", "100"}, {"type", "int"}, {"evaluateName", "a"}, {"variablesReference", 0}}, + {{"name", "b"}, + {"value", "1000"}, + {"type", "int"}, + {"evaluateName", "b"}, + {"variablesReference", 0}}}}}} }; return reply; } @@ -372,17 +317,16 @@ namespace xcpp { // Placeholder DAP response std::cout << "debugger::attach_request" << std::endl; + std::cout << "Message: " << message.dump() << std::endl; nl::json attach_request = { {"seq", 2}, {"type", "request"}, {"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())} + {"pid", jit_process_pid} }} }; + std::cout << "Sending attach request: " << attach_request.dump() << std::endl; nl::json reply = forward_message(attach_request); std::cout << "Attach request sent: " << reply.dump() << std::endl; return reply; diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index d5966b13..f730bfab 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -46,9 +46,17 @@ void* createInterpreter(const Args &ExtraArgs = {}) { 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"}*/); + if (std::find_if(ExtraArgs.begin(), ExtraArgs.end(), [](const std::string& s) { + return s == "-gdwarf-4";}) == ExtraArgs.end()) { + // If no debugger option, then use the non-OOP JIT execution. + return Cpp::CreateInterpreter(ClangArgs/*, {"-cuda"}*/); + } + + // If debugger option is set, then use the OOP JIT execution. + return Cpp::CreateInterpreter(ClangArgs, {}, true); } using namespace std::placeholders; @@ -56,15 +64,14 @@ using namespace std::placeholders; namespace xcpp { struct StreamRedirectRAII { - std::string &err; - StreamRedirectRAII(std::string &e) : err(e) { + std::string &out, &err; + StreamRedirectRAII(std::string &o, std::string &e) : out(o), err(e) { Cpp::BeginStdStreamCapture(Cpp::kStdErr); Cpp::BeginStdStreamCapture(Cpp::kStdOut); } ~StreamRedirectRAII() { - std::string out = Cpp::EndStdStreamCapture(); + out = Cpp::EndStdStreamCapture(); err = Cpp::EndStdStreamCapture(); - std::cout << out; } }; @@ -73,6 +80,33 @@ namespace xcpp xeus::register_interpreter(this); } + std::string interpreter::get_current_pid() + { + const char* code = R"( +#include +#include +std::cout << getpid() << std::endl; + )"; + + std::string pid_str, err; + bool compilation_result = false; + { + StreamRedirectRAII R(pid_str, err); + compilation_result = Cpp::Process(code); + } + if (compilation_result) { + std::cerr << "Error getting OOP JIT pid: " << err << std::endl; + return ""; + } else { + std::cout << "Current OOP JIT PID: " << pid_str << std::endl; + } + // Remove trailing newline from pid_str if present + if (!pid_str.empty() && pid_str.back() == '\n') { + pid_str.pop_back(); + } + return pid_str; + } + static std::string get_stdopt() { // We need to find what's the C++ version the interpreter runs with. @@ -109,7 +143,7 @@ __get_cxx_version () { //NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); - m_version = get_stdopt(); + // m_version = get_stdopt(); redirect_output(); init_preamble(); init_magic(); @@ -162,12 +196,12 @@ __get_cxx_version () std::cerr.rdbuf(&null); } - std::string err; + std::string out, err; // Attempt normal evaluation try { - StreamRedirectRAII R(err); + StreamRedirectRAII R(out, err); compilation_result = Cpp::Process(code.c_str()); } catch (std::exception& e) @@ -188,6 +222,9 @@ __get_cxx_version () ename = "Error: "; evalue = "Compilation error! " + err; std::cerr << err; + } else { + std::cout << "EHLLOE" << std::endl; + std::cout << out; } // Flush streams From be3785302df6c1dc91e44ccd3e8116aca242c5d4 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 19 Jun 2025 20:58:03 +0530 Subject: [PATCH 11/19] out-of-process JIT execution --- include/xeus-cpp/xdebugger.hpp | 2 +- include/xeus-cpp/xinterpreter.hpp | 2 +- src/xdebugger.cpp | 239 +++++---------------- src/xinterpreter.cpp | 334 ++++++++++++++++-------------- 4 files changed, 230 insertions(+), 347 deletions(-) diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp index 40874bb7..e15203ff 100644 --- a/include/xeus-cpp/xdebugger.hpp +++ b/include/xeus-cpp/xdebugger.hpp @@ -69,7 +69,7 @@ namespace xcpp bool m_is_running; int m_tcp_socket; bool m_tcp_connected; - std::string jit_process_pid; + pid_t jit_process_pid; }; XEUS_CPP_API diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index 0d2332b8..d0694bb3 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -40,7 +40,7 @@ namespace xcpp void publish_stdout(const std::string&); void publish_stderr(const std::string&); - static std::string get_current_pid(); + static pid_t get_current_pid(); private: diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 27bbebbf..22fe0476 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -61,197 +61,97 @@ namespace xcpp debugger::~debugger() { - std::cout << "Stopping debugger..........." << std::endl; delete p_debuglldb_client; p_debuglldb_client = nullptr; } bool debugger::start_lldb() { - std::cout << "debugger::start_lldb" << std::endl; - jit_process_pid = xcpp::interpreter::get_current_pid(); + jit_process_pid = interpreter::get_current_pid(); - // 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; + std::cerr << "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) + if (std::getenv("XEUS_LOG")) { - std::ofstream out("xeus.log", std::ios_base::app); - out << "===== DEBUGGER CONFIG =====" << std::endl; - out << m_debugger_config.dump() << std::endl; + std::ofstream log("xeus.log", std::ios::app); + log << "===== DEBUGGER CONFIG =====\n"; + log << m_debugger_config.dump(4) << '\n'; } - // 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"; + std::vector lldb_args = {"lldb-dap", "--port", m_lldb_port}; - // 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()) - { - 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; - // 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"; - } - } - } - - // 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"; - // 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"; + pid_t pid = fork(); + if (pid == 0) + { + int fd = open(log_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd != -1) + { + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + close(fd); + } - std::cout << "Starting LLDB-DAP with port: " << m_lldb_port << std::endl; + int null_fd = open("/dev/null", O_RDONLY); + if (null_fd != -1) + { + dup2(null_fd, STDIN_FILENO); + close(null_fd); + } - // 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::vector argv; + for (auto& arg : lldb_args) + { + argv.push_back(const_cast(arg.c_str())); + } + argv.push_back(nullptr); - std::cout << "LLDB-DAP start response: " << rep.dump() << std::endl; + execvp("lldb-dap", argv.data()); - if (status != "ok") + std::cerr << "Failed to execute lldb-dap" << std::endl; + std::exit(1); + } + else if (pid > 0) { - 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) + int status; + if (waitpid(pid, &status, WNOHANG) != 0) { - std::clog << traceback[i] << std::endl; + std::cerr << "LLDB-DAP process exited prematurely." << std::endl; + return false; } - std::clog << ename << " - " << evalue << std::endl; - return false; + m_is_running = true; + return true; } else { - std::cout << xcpp::green_text("LLDB-DAP process started successfully") << std::endl; + std::cerr << "fork() failed" << std::endl; + return false; } - - m_is_running = true; - return status == "ok"; } bool debugger::start() { - std::cout << "Starting debugger..." << std::endl; - - // Start LLDB-DAP process static bool lldb_started = start_lldb(); if (!lldb_started) { - std::cout << "Failed to start LLDB-DAP" << std::endl; + std::cerr << "Failed to start LLDB-DAP" << std::endl; return false; } - // 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); - 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, @@ -263,24 +163,28 @@ namespace xcpp ); client.detach(); - // Also test ZMQ path send_recv_request("REQ"); - // std::cout << forward_message(init_request).dump() << std::endl; - - // 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::attach_request(const nl::json& message) + { + // Placeholder DAP response + nl::json attach_request = + {{"seq", 2}, {"type", "request"}, {"command", "attach"}, {"arguments", {{"pid", jit_process_pid}}}}; + std::cout << "Sending attach request: " << attach_request.dump() << std::endl; + nl::json reply = forward_message(attach_request); + return reply; + } + nl::json debugger::inspect_variables_request(const nl::json& message) { std::cout << "[debugger::inspect_variables_request] inspect_variables_request not implemented" << std::endl; - std::cout << message.dump() << std::endl; nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -300,8 +204,6 @@ 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"]}, @@ -313,29 +215,8 @@ namespace xcpp return reply; } - nl::json debugger::attach_request(const nl::json& message) - { - // Placeholder DAP response - std::cout << "debugger::attach_request" << std::endl; - std::cout << "Message: " << message.dump() << std::endl; - nl::json attach_request = { - {"seq", 2}, - {"type", "request"}, - {"command", "attach"}, - {"arguments", { - {"pid", jit_process_pid} - }} - }; - std::cout << "Sending attach request: " << attach_request.dump() << std::endl; - nl::json reply = forward_message(attach_request); - std::cout << "Attach request sent: " << reply.dump() << std::endl; - return reply; - } - 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"]}, @@ -347,8 +228,6 @@ 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"]}, @@ -362,8 +241,6 @@ namespace xcpp 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); @@ -372,7 +249,6 @@ 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(), @@ -386,7 +262,6 @@ 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; @@ -402,12 +277,6 @@ namespace xcpp const nl::json& debugger_config ) { - 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( new debugger(context, config, user_name, session_id, debugger_config) ); diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index f730bfab..bbd9502e 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -27,52 +27,80 @@ 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"; +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. - if (std::find_if(ExtraArgs.begin(), ExtraArgs.end(), [](const std::string& s) { - return s == "-gdwarf-4";}) == ExtraArgs.end()) { - // If no debugger option, then use the non-OOP JIT execution. - return Cpp::CreateInterpreter(ClangArgs/*, {"-cuda"}*/); - } - - // If debugger option is set, then use the OOP JIT execution. - return Cpp::CreateInterpreter(ClangArgs, {}, true); + 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. + if (std::find_if( + ExtraArgs.begin(), + ExtraArgs.end(), + [](const std::string& s) + { + return s == "-gdwarf-4"; + } + ) + == ExtraArgs.end()) + { + // If no debugger option, then use the non-OOP JIT execution. + return Cpp::CreateInterpreter(ClangArgs /*, {"-cuda"}*/); + } + + // If debugger option is set, then use the OOP JIT execution. + return Cpp::CreateInterpreter(ClangArgs, {}, true); } using namespace std::placeholders; namespace xcpp { - struct StreamRedirectRAII { - std::string &out, &err; - StreamRedirectRAII(std::string &o, std::string &e) : out(o), err(e) { - Cpp::BeginStdStreamCapture(Cpp::kStdErr); - Cpp::BeginStdStreamCapture(Cpp::kStdOut); - } - ~StreamRedirectRAII() { - out = Cpp::EndStdStreamCapture(); - err = Cpp::EndStdStreamCapture(); - } + struct StreamRedirectRAII + { + std::string &out, &err; + + StreamRedirectRAII(std::string& o, std::string& e) + : out(o) + , err(e) + { + Cpp::BeginStdStreamCapture(Cpp::kStdErr); + Cpp::BeginStdStreamCapture(Cpp::kStdOut); + } + + ~StreamRedirectRAII() + { + out = Cpp::EndStdStreamCapture(); + err = Cpp::EndStdStreamCapture(); + } }; void interpreter::configure_impl() @@ -80,31 +108,9 @@ namespace xcpp xeus::register_interpreter(this); } - std::string interpreter::get_current_pid() + pid_t interpreter::get_current_pid() { - const char* code = R"( -#include -#include -std::cout << getpid() << std::endl; - )"; - - std::string pid_str, err; - bool compilation_result = false; - { - StreamRedirectRAII R(pid_str, err); - compilation_result = Cpp::Process(code); - } - if (compilation_result) { - std::cerr << "Error getting OOP JIT pid: " << err << std::endl; - return ""; - } else { - std::cout << "Current OOP JIT PID: " << pid_str << std::endl; - } - // Remove trailing newline from pid_str if present - if (!pid_str.empty() && pid_str.back() == '\n') { - pid_str.pop_back(); - } - return pid_str; + return Cpp::GetExecutorPID(); } static std::string get_stdopt() @@ -134,14 +140,14 @@ __get_cxx_version () return std::to_string(cxx_version); } - interpreter::interpreter(int argc, const char* const* argv) : - xmagics() + interpreter::interpreter(int argc, const char* const* argv) + : xmagics() , p_cout_strbuf(nullptr) , p_cerr_strbuf(nullptr) , m_cout_buffer(std::bind(&interpreter::publish_stdout, this, _1)) , m_cerr_buffer(std::bind(&interpreter::publish_stderr, this, _1)) { - //NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) + // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); // m_version = get_stdopt(); redirect_output(); @@ -222,8 +228,9 @@ __get_cxx_version () ename = "Error: "; evalue = "Compilation error! " + err; std::cerr << err; - } else { - std::cout << "EHLLOE" << std::endl; + } + else + { std::cout << out; } @@ -247,10 +254,11 @@ __get_cxx_version () // // JupyterLab displays the "{ename}: {evalue}" if the traceback is // empty. - if (evalue.size() < 4) { + if (evalue.size() < 4) + { ename = " "; } - std::vector traceback({ename + evalue}); + std::vector traceback({ename + evalue}); if (!config.silent) { publish_execution_error(ename, evalue, traceback); @@ -293,7 +301,8 @@ __get_cxx_version () Cpp::CodeComplete(results, code.c_str(), 1, _cursor_pos + 1); - return xeus::create_complete_reply(results /*matches*/, + return xeus::create_complete_reply( + results /*matches*/, cursor_pos - to_complete.length() /*cursor_start*/, cursor_pos /*cursor_end*/ ); @@ -314,13 +323,17 @@ __get_cxx_version () nl::json interpreter::is_complete_request_impl(const std::string& code) { - if (!code.empty() && code[code.size() - 1] == '\\') { + if (!code.empty() && code[code.size() - 1] == '\\') + { auto found = code.rfind('\n'); if (found == std::string::npos) + { found = -1; + } auto found1 = found++; - while (isspace(code[++found1])) ; - return xeus::create_is_complete_reply("incomplete", code.substr(found, found1-found)); + while (isspace(code[++found1])) + ; + return xeus::create_is_complete_reply("incomplete", code.substr(found, found1 - found)); } return xeus::create_is_complete_reply("complete"); @@ -369,109 +382,110 @@ __get_cxx_version () } 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()) + std::string code = content.value("code", ""); + nl::json reply; + try { - throw std::runtime_error("Failed to create temporary source file"); - } + // 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(); + 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; + // 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_msg += line + "\n"; + 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()); - 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"; + 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"; + } } - 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) + catch (const std::exception& e) { reply["status"] = "error"; - reply["ename"] = "RuntimeError"; - reply["evalue"] = "C++ program execution failed"; - reply["traceback"] = std::vector{error_output}; - } - else - { - reply["status"] = "ok"; + reply["ename"] = "SystemError"; + reply["evalue"] = e.what(); + reply["traceback"] = std::vector{e.what()}; } - } - catch (const std::exception& e) - { - reply["status"] = "error"; - reply["ename"] = "SystemError"; - reply["evalue"] = e.what(); - reply["traceback"] = std::vector{e.what()}; - } - return reply; -} + return reply; + } void interpreter::redirect_output() { @@ -500,11 +514,11 @@ __get_cxx_version () void interpreter::init_preamble() { - //NOLINTBEGIN(cppcoreguidelines-owning-memory) + // NOLINTBEGIN(cppcoreguidelines-owning-memory) preamble_manager.register_preamble("introspection", std::make_unique()); preamble_manager.register_preamble("magics", std::make_unique()); preamble_manager.register_preamble("shell", std::make_unique()); - //NOLINTEND(cppcoreguidelines-owning-memory) + // NOLINTEND(cppcoreguidelines-owning-memory) } void interpreter::init_magic() From 73fd7d53b66381456de22c673e4df98dd61b3327 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 19 Jun 2025 21:02:03 +0530 Subject: [PATCH 12/19] removed some redundant files --- .vscode/settings.json | 54 -- test/Notebooks/xeus-cpp_output.ipynb | 811 --------------------------- 2 files changed, 865 deletions(-) delete mode 100644 .vscode/settings.json delete mode 100644 test/Notebooks/xeus-cpp_output.ipynb diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 370a27a3..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "files.associations": { - "iostream": "cpp", - "fstream": "cpp", - "ios": "cpp", - "locale": "cpp", - "sstream": "cpp", - "__bit_reference": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__node_handle": "cpp", - "__split_buffer": "cpp", - "__tree": "cpp", - "__verbose_abort": "cpp", - "array": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "initializer_list": "cpp", - "iosfwd": "cpp", - "istream": "cpp", - "limits": "cpp", - "map": "cpp", - "mutex": "cpp", - "new": "cpp", - "print": "cpp", - "regex": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "string_view": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "vector": "cpp", - "cstdarg": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "execution": "cpp", - "iomanip": "cpp", - "optional": "cpp", - "queue": "cpp", - "ratio": "cpp", - "stack": "cpp", - "variant": "cpp" - } -} \ No newline at end of file diff --git a/test/Notebooks/xeus-cpp_output.ipynb b/test/Notebooks/xeus-cpp_output.ipynb deleted file mode 100644 index 4f61dbdd..00000000 --- a/test/Notebooks/xeus-cpp_output.ipynb +++ /dev/null @@ -1,811 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "be1c8c6c-3fbe-4f53-9deb-8496c43d26ad", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "## Output and error streams\n", - "\n", - "`std::cout` and `std::cerr` are redirected to the notebook frontend." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "56c6f89e-205e-4099-bfbe-4d84e45f4390", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "#include " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bd7f767-6c22-4a1b-a1e2-cd4184fd0367", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "std::cout << \"some output\";" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9622f20f-5925-4544-a97b-aada3a14209a", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "std::cerr << \"some error\";" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7af0c962-17dc-47d4-9772-b8a06e2bda3a", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "int j = 5;\n", - "std::cout << j << std::endl;" - ] - }, - { - "cell_type": "markdown", - "id": "526a7dba-4001-47d5-a116-95423118e100", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "# Interpreting the C++ programming language\n", - "\n", - "You can define functions, classes, templates, etc ..." - ] - }, - { - "cell_type": "markdown", - "id": "e5b116ce-ced1-4aa4-b14e-ef7d2606202e", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "## Functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86b08f22-e16c-4eac-917d-ae6eeb7ec7cb", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "double sqr(double a)\n", - "{\n", - " return a * a;\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5aff6711-54bf-4280-a496-c9f7c683eee5", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "double a = 2.5;\n", - "double asqr = sqr(a);\n", - "std::cout << asqr << std::endl;" - ] - }, - { - "cell_type": "markdown", - "id": "5b3959b0-9dc7-41a4-bba1-e20abd0765f7", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "## Classes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d981a53b-8185-49c5-8a30-02453cc1b9e9", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "class Foo\n", - "{\n", - "public:\n", - "\n", - " virtual ~Foo() {}\n", - " \n", - " virtual void print(double value) const\n", - " {\n", - " std::cout << \"Foo value = \" << value << std::endl;\n", - " }\n", - "};" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "195d513f-d5cb-4e3d-a6cb-ae99dfcd9aab", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "Foo bar;\n", - "bar.print(1.2);" - ] - }, - { - "cell_type": "markdown", - "id": "9ecc1588-cb6e-4676-bb16-b9938e980b06", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "## Polymorphism" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4df90bea-5c9e-462e-bd20-d80fd169b7b6", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "class Bar : public Foo\n", - "{\n", - "public:\n", - "\n", - " virtual ~Bar() {}\n", - " \n", - " virtual void print(double value) const\n", - " {\n", - " std::cout << \"Bar value = \" << 2 * value << std::endl;\n", - " }\n", - "};" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f7dbbcc2-0f00-48eb-8bb9-94e871afa2a7", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "Foo* bar2 = new Bar;\n", - "bar2->print(1.2);\n", - "delete bar2;" - ] - }, - { - "cell_type": "markdown", - "id": "094f4ca7-0aa5-4121-bfff-bf5db1d42c5d", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "## Templates" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0df4f3a5-25a1-4548-ba63-54887c770dad", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "#include \n", - "\n", - "template \n", - "class FooT\n", - "{\n", - "public:\n", - " \n", - " explicit FooT(const T& t) : m_t(t) {}\n", - " \n", - " void print() const\n", - " {\n", - " std::cout << typeid(T).name() << \" m_t = \" << m_t << std::endl;\n", - " }\n", - " \n", - "private:\n", - " \n", - " T m_t;\n", - "};" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e7bcab70-b9db-409c-aa04-9c64b413e266", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "FooT foot(1.2);\n", - "foot.print();" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de6ba06d-ed19-40b6-b31d-b8782fb81174", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "#include \"xcpp/xdisplay.hpp\"" - ] - }, - { - "cell_type": "markdown", - "id": "387d6a7a-7ca4-41d6-9809-4040641db338", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "### Audio example" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5aab5534-8420-4341-bf59-546d5f24bbed", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "#include \n", - "#include \n", - "\n", - "#include \"nlohmann/json.hpp\"\n", - "\n", - "#include \"xeus/xbase64.hpp\"\n", - "\n", - "namespace nl = nlohmann;\n", - "\n", - "namespace au\n", - "{\n", - " struct audio\n", - " { \n", - " inline audio(const std::string& filename)\n", - " {\n", - " std::ifstream fin(filename, std::ios::binary); \n", - " m_buffer << fin.rdbuf();\n", - " }\n", - " \n", - " std::stringstream m_buffer;\n", - " };\n", - " \n", - " nl::json mime_bundle_repr(const audio& a)\n", - " {\n", - " auto bundle = nl::json::object();\n", - " bundle[\"text/html\"] =\n", - " std::string(\"\";\n", - " return bundle;\n", - " }\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b9e8220-fe5a-498e-93a1-a93e71bda629", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "au::audio drums(\"audio/audio.wav\");" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "faee9bb7-22e4-49ad-8483-38cc1a044b19", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "xcpp::display(drums);" - ] - }, - { - "cell_type": "markdown", - "id": "d32274d3-61eb-4763-9b10-b3d4db09ae5c", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "### Update-display" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5ba5126e-ea3a-4393-84a1-99fac70ae5e4", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "#include \n", - "#include \"xcpp/xdisplay.hpp\"\n", - "\n", - "#include \"nlohmann/json.hpp\"\n", - "\n", - "namespace nl = nlohmann;\n", - "\n", - "namespace ht\n", - "{\n", - " struct html\n", - " { \n", - " inline html(const std::string& content)\n", - " {\n", - " m_content = content;\n", - " }\n", - " std::string m_content;\n", - " };\n", - "\n", - " nl::json mime_bundle_repr(const html& a)\n", - " {\n", - " auto bundle = nl::json::object();\n", - " bundle[\"text/html\"] = a.m_content;\n", - " return bundle;\n", - " }\n", - "}\n", - "\n", - "// A blue rectangle\n", - "ht::html rect(R\"(\n", - "
\n", - "Original\n", - "
)\");" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a3de3009-bf70-4adf-9471-900be0bc3d2d", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "xcpp::display(rect, \"some_display_id\");" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "629fa3d5-6c5d-40ad-937d-6257ae6c827d", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "// Update the rectangle to be red\n", - "rect.m_content = R\"(\n", - "
\n", - "Updated\n", - "
)\";\n", - "\n", - "xcpp::display(rect, \"some_display_id\", true);" - ] - }, - { - "cell_type": "markdown", - "id": "d4828c73-b061-4f8f-9966-f45d570c6567", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [] - }, - "source": [ - "### Clear output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8391fa79-f365-4792-b1f6-b2f68d79a3d6", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "#include \n", - "#include \n", - "#include \n", - "\n", - "#include \"xcpp/xdisplay.hpp\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16ab8930-6639-4c8d-855f-58ec70a62c17", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "std::cout << \"hello\" << std::endl;\n", - "std::this_thread::sleep_for(std::chrono::seconds(1));\n", - "xcpp::clear_output(); // will flicker when replacing \"hello\" with \"goodbye\"\n", - "std::this_thread::sleep_for(std::chrono::seconds(1));\n", - "std::cout << \"goodbye\" << std::endl;" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "347331f2-98cf-4111-989c-7e34b3d0a309", - "metadata": { - "papermill": { - "duration": null, - "end_time": null, - "exception": null, - "start_time": null, - "status": "completed" - }, - "tags": [], - "vscode": { - "languageId": "c++" - } - }, - "outputs": [], - "source": [ - "std::cout << \"hello\" << std::endl;\n", - "std::this_thread::sleep_for(std::chrono::seconds(1));\n", - "xcpp::clear_output(true); // prevents flickering\n", - "std::this_thread::sleep_for(std::chrono::seconds(1));\n", - "std::cout << \"goodbye\" << std::endl;" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "C++20", - "language": "cpp", - "name": "xcpp20" - }, - "language_info": { - "codemirror_mode": "text/x-c++src", - "file_extension": ".cpp", - "mimetype": "text/x-c++src", - "name": "C++", - "version": "20" - }, - "papermill": { - "default_parameters": {}, - "duration": 1.02855, - "end_time": "2025-05-08T08:42:49.221498", - "environment_variables": {}, - "exception": null, - "input_path": "Notebooks/xeus-cpp.ipynb", - "output_path": "Notebooks/xeus-cpp_output.ipynb", - "parameters": {}, - "start_time": "2025-05-08T08:42:48.192948", - "version": "2.6.0" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file From 78e14aafe7a7b48c1c18fea289a0b82e0650384a Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 19 Jun 2025 22:14:00 +0530 Subject: [PATCH 13/19] added XEUS_CPP_USE_DEBUGGER var --- CMakeLists.txt | 38 ++++++++++++------ src/main.cpp | 19 ++++++--- src/xinterpreter.cpp | 94 ++++++++++++++++---------------------------- 3 files changed, 75 insertions(+), 76 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ceef6828..5e4f84c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,7 +180,9 @@ 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/") + if(XEUS_CPP_USE_DEBUGGER) + configure_native_kernel("/share/jupyter/kernels/xcpp17-debugger/") + endif() endif() # Source files @@ -197,12 +199,17 @@ 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 ) +if(XEUS_CPP_USE_DEBUGGER) + list(APPEND XEUS_CPP_HEADERS + include/xeus-cpp/xdebugger.hpp + ) +endif() + set(XEUS_CPP_SRC src/xholder.cpp src/xinput.cpp @@ -212,9 +219,6 @@ 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) @@ -223,6 +227,14 @@ if(NOT EMSCRIPTEN) ) endif() +if(XEUS_CPP_USE_DEBUGGER) + list(APPEND XEUS_CPP_SRC + src/xdebugger.cpp + src/xdebuglldb_client.cpp + src/xinternal_utils.cpp + ) +endif() + if(EMSCRIPTEN) list(APPEND XEUS_CPP_SRC src/xinterpreter_wasm.cpp) endif() @@ -473,13 +485,11 @@ include(CMakePackageConfigHelpers) set(XEUS_CPP_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING "install path for xeus-cppConfig.cmake") -if(NOT EMSCRIPTEN) - install(DIRECTORY ${XCPP_TAGFILES_DIR} - DESTINATION ${XEUS_CPP_DATA_DIR}) +install(DIRECTORY ${XCPP_TAGFILES_DIR} + DESTINATION ${XEUS_CPP_DATA_DIR}) - install(DIRECTORY ${XCPP_TAGCONFS_DIR} - DESTINATION ${XEUS_CPP_CONF_DIR}) -endif() +install(DIRECTORY ${XCPP_TAGCONFS_DIR} + DESTINATION ${XEUS_CPP_CONF_DIR}) # Install xeus-cpp and xeus-cpp-static if (XEUS_CPP_BUILD_SHARED) @@ -554,3 +564,9 @@ if(EMSCRIPTEN) "$/xcpp.data" DESTINATION ${CMAKE_INSTALL_BINDIR}) endif () + +if(XEUS_CPP_USE_DEBUGGER) + add_definitions( + -DXEUS_CPP_USE_DEBUGGER + ) +endif() \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index dffb2216..1b2f8e9b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,11 +20,11 @@ #endif #include "nlohmann/json.hpp" -#include "xeus/xhelper.hpp" -#include "xeus/xkernel.hpp" -#include "xeus/xkernel_configuration.hpp" +#include +#include +#include #include "xeus-zmq/xzmq_context.hpp" -#include "xeus-zmq/xserver_zmq.hpp" +#include #include "xeus-cpp/xeus_cpp_config.hpp" #include "xeus-cpp/xinterpreter.hpp" #include "xeus-cpp/xutils.hpp" @@ -60,11 +60,12 @@ int main(int argc, char* argv[]) #endif signal(SIGINT, xcpp::stop_handler); - // Debugger configuration for LLDB-DAP +#ifdef XEUS_CPP_USE_DEBUGGER nl::json debugger_config; debugger_config["lldb"]["initCommands"] = { "settings set plugin.jit-loader.gdb.enable on" }; +#endif std::string file_name = xeus::extract_filename(argc, argv); auto interpreter = std::make_unique(argc, argv); @@ -85,9 +86,13 @@ int main(int argc, char* argv[]) xeus::make_console_logger( xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log") +#ifdef XEUS_CPP_USE_DEBUGGER ), xcpp::make_cpp_debugger, debugger_config +#else + ) +#endif ); std::clog << "Starting xcpp kernel...\n\n" @@ -108,9 +113,13 @@ int main(int argc, char* argv[]) xeus::make_console_logger( xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log") +#ifdef XEUS_CPP_USE_DEBUGGER ), xcpp::make_cpp_debugger, debugger_config +#else + ) +#endif ); std::cout << "Getting config" << std::endl; diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index bbd9502e..561c19d3 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -29,32 +29,20 @@ 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()) - { + 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()) - { + if (!resource_dir.empty()) { ClangArgs.push_back("-resource-dir"); ClangArgs.push_back(resource_dir.c_str()); - } - else - { + } else { std::cerr << "Failed to detect the resource-dir\n"; } } std::vector CxxSystemIncludes; Cpp::DetectSystemCompilerIncludePaths(CxxSystemIncludes); - for (const std::string& CxxInclude : CxxSystemIncludes) - { + for (const std::string& CxxInclude : CxxSystemIncludes) { ClangArgs.push_back("-isystem"); ClangArgs.push_back(CxxInclude.c_str()); } @@ -62,45 +50,35 @@ void* createInterpreter(const Args& ExtraArgs = {}) // FIXME: We should process the kernel input options and conditionally pass // the gpu args here. - if (std::find_if( - ExtraArgs.begin(), - ExtraArgs.end(), - [](const std::string& s) - { - return s == "-gdwarf-4"; - } - ) - == ExtraArgs.end()) - { + if (std::find_if(ExtraArgs.begin(), ExtraArgs.end(), [](const std::string& s) { + return s == "-gdwarf-4";}) == ExtraArgs.end()) { // If no debugger option, then use the non-OOP JIT execution. return Cpp::CreateInterpreter(ClangArgs /*, {"-cuda"}*/); } - // If debugger option is set, then use the OOP JIT execution. +#ifdef XEUS_CPP_USE_DEBUGGER +// If debugger option is set, then use the OOP JIT execution. return Cpp::CreateInterpreter(ClangArgs, {}, true); +#else + return Cpp::CreateInterpreter(ClangArgs); +#endif } using namespace std::placeholders; namespace xcpp { - struct StreamRedirectRAII - { - std::string &out, &err; - - StreamRedirectRAII(std::string& o, std::string& e) - : out(o) - , err(e) - { - Cpp::BeginStdStreamCapture(Cpp::kStdErr); - Cpp::BeginStdStreamCapture(Cpp::kStdOut); - } - - ~StreamRedirectRAII() - { - out = Cpp::EndStdStreamCapture(); - err = Cpp::EndStdStreamCapture(); - } + 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() @@ -108,10 +86,12 @@ namespace xcpp xeus::register_interpreter(this); } +#ifdef XEUS_CPP_USE_DEBUGGER pid_t interpreter::get_current_pid() { return Cpp::GetExecutorPID(); } +#endif static std::string get_stdopt() { @@ -140,8 +120,8 @@ __get_cxx_version () return std::to_string(cxx_version); } - interpreter::interpreter(int argc, const char* const* argv) - : xmagics() + interpreter::interpreter(int argc, const char* const* argv): + xmagics() , p_cout_strbuf(nullptr) , p_cerr_strbuf(nullptr) , m_cout_buffer(std::bind(&interpreter::publish_stdout, this, _1)) @@ -202,12 +182,12 @@ __get_cxx_version () std::cerr.rdbuf(&null); } - std::string out, err; + std::string err; // Attempt normal evaluation try { - StreamRedirectRAII R(out, err); + StreamRedirectRAII R(err); compilation_result = Cpp::Process(code.c_str()); } catch (std::exception& e) @@ -229,10 +209,6 @@ __get_cxx_version () evalue = "Compilation error! " + err; std::cerr << err; } - else - { - std::cout << out; - } // Flush streams std::cout << std::flush; @@ -254,8 +230,7 @@ __get_cxx_version () // // JupyterLab displays the "{ename}: {evalue}" if the traceback is // empty. - if (evalue.size() < 4) - { + if (evalue.size() < 4) { ename = " "; } std::vector traceback({ename + evalue}); @@ -301,8 +276,7 @@ __get_cxx_version () Cpp::CodeComplete(results, code.c_str(), 1, _cursor_pos + 1); - return xeus::create_complete_reply( - results /*matches*/, + return xeus::create_complete_reply(results /*matches*/, cursor_pos - to_complete.length() /*cursor_start*/, cursor_pos /*cursor_end*/ ); @@ -514,11 +488,11 @@ __get_cxx_version () void interpreter::init_preamble() { - // NOLINTBEGIN(cppcoreguidelines-owning-memory) + //NOLINTBEGIN(cppcoreguidelines-owning-memory) preamble_manager.register_preamble("introspection", std::make_unique()); preamble_manager.register_preamble("magics", std::make_unique()); preamble_manager.register_preamble("shell", std::make_unique()); - // NOLINTEND(cppcoreguidelines-owning-memory) + //NOLINTEND(cppcoreguidelines-owning-memory) } void interpreter::init_magic() From 3c6be4e07989ec8bf1e4f5888acba80c96dff5a1 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Thu, 19 Jun 2025 22:23:27 +0530 Subject: [PATCH 14/19] added XEUS_CPP_USE_DEBUGGER var --- src/xinterpreter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index 561c19d3..c55c2ed8 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -129,7 +129,11 @@ __get_cxx_version () { // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); +#ifdef XEUS_CPP_USE_DEBUGGER // m_version = get_stdopt(); +#else + m_version = get_stdopt(); +#endif redirect_output(); init_preamble(); init_magic(); From 90df7377f775b717b41ec3cc894f121e1f5386cb Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Mon, 23 Jun 2025 12:33:58 +0530 Subject: [PATCH 15/19] added breakpoints --- CMakeLists.txt | 28 +--- include/xeus-cpp/xdebugger.hpp | 14 +- include/xeus-cpp/xinterpreter.hpp | 14 +- .../kernels/xcpp17-debugger/kernel.json.in | 2 +- src/main.cpp | 19 +-- src/xdebugger.cpp | 128 ++++++++++++++---- src/xinternal_utils.cpp | 7 - src/xinternal_utils.hpp | 3 - src/xinterpreter.cpp | 123 +---------------- 9 files changed, 137 insertions(+), 201 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e4f84c6..9a649784 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,9 +180,7 @@ else() configure_native_kernel("/share/jupyter/kernels/xcpp17/") configure_native_kernel("/share/jupyter/kernels/xcpp20/") configure_native_kernel("/share/jupyter/kernels/xcpp23/") - if(XEUS_CPP_USE_DEBUGGER) - configure_native_kernel("/share/jupyter/kernels/xcpp17-debugger/") - endif() + configure_native_kernel("/share/jupyter/kernels/xcpp17-debugger/") endif() # Source files @@ -199,17 +197,12 @@ 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 ) -if(XEUS_CPP_USE_DEBUGGER) - list(APPEND XEUS_CPP_HEADERS - include/xeus-cpp/xdebugger.hpp - ) -endif() - set(XEUS_CPP_SRC src/xholder.cpp src/xinput.cpp @@ -219,6 +212,9 @@ set(XEUS_CPP_SRC src/xparser.cpp src/xutils.cpp src/xmagics/os.cpp + src/xdebugger.cpp + src/xdebuglldb_client.cpp + src/xinternal_utils.cpp ) if(NOT EMSCRIPTEN) @@ -227,14 +223,6 @@ if(NOT EMSCRIPTEN) ) endif() -if(XEUS_CPP_USE_DEBUGGER) - list(APPEND XEUS_CPP_SRC - src/xdebugger.cpp - src/xdebuglldb_client.cpp - src/xinternal_utils.cpp - ) -endif() - if(EMSCRIPTEN) list(APPEND XEUS_CPP_SRC src/xinterpreter_wasm.cpp) endif() @@ -564,9 +552,3 @@ if(EMSCRIPTEN) "$/xcpp.data" DESTINATION ${CMAKE_INSTALL_BINDIR}) endif () - -if(XEUS_CPP_USE_DEBUGGER) - add_definitions( - -DXEUS_CPP_USE_DEBUGGER - ) -endif() \ No newline at end of file diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp index e15203ff..240e61d9 100644 --- a/include/xeus-cpp/xdebugger.hpp +++ b/include/xeus-cpp/xdebugger.hpp @@ -23,6 +23,7 @@ #include "nlohmann/json.hpp" #include "xeus-zmq/xdebugger_base.hpp" #include "xeus_cpp_config.hpp" +#include "xeus-cpp/xinterpreter.hpp" namespace xcpp { @@ -48,6 +49,8 @@ namespace xcpp 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 set_breakpoints_request(const nl::json& message); + nl::json dump_cell_request(const nl::json& message); nl::json variables_request_impl(const nl::json& message) override; @@ -55,7 +58,7 @@ namespace xcpp 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; + virtual 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); @@ -64,12 +67,19 @@ namespace xcpp 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; pid_t jit_process_pid; + + using breakpoint_list_t = std::map>; + breakpoint_list_t m_breakpoint_list; + + xcpp::interpreter* m_interpreter; + + std::unordered_map> m_hash_to_sequential; + std::unordered_map m_sequential_to_hash; }; XEUS_CPP_API diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index d0694bb3..1577751d 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -42,6 +42,16 @@ namespace xcpp static pid_t get_current_pid(); + std::vector get_execution_count(const std::string& code) const + { + auto it = m_code_to_execution_count_map.find(code); + if (it != m_code_to_execution_count_map.end()) + { + return it->second; + } + return {}; + } + private: void configure_impl() override; @@ -64,8 +74,6 @@ 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, @@ -89,6 +97,8 @@ namespace xcpp xoutput_buffer m_cout_buffer; xoutput_buffer m_cerr_buffer; + + std::map> m_code_to_execution_count_map; }; } diff --git a/share/jupyter/kernels/xcpp17-debugger/kernel.json.in b/share/jupyter/kernels/xcpp17-debugger/kernel.json.in index 1f1d839e..90d5c13d 100644 --- a/share/jupyter/kernels/xcpp17-debugger/kernel.json.in +++ b/share/jupyter/kernels/xcpp17-debugger/kernel.json.in @@ -8,7 +8,7 @@ "@XEUS_CPP_KERNELSPEC_PATH@xcpp", "-f", "{connection_file}", - "-gdwarf-4", + "-g", "-O0", "-resource-dir", "@XEUS_CPP_RESOURCE_DIR@", "-I", "@XEUS_CPP_INCLUDE_DIR@", diff --git a/src/main.cpp b/src/main.cpp index 1b2f8e9b..1dcfaf7d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,16 +60,15 @@ int main(int argc, char* argv[]) #endif signal(SIGINT, xcpp::stop_handler); -#ifdef XEUS_CPP_USE_DEBUGGER + std::string file_name = xeus::extract_filename(argc, argv); + auto interpreter = std::make_unique(argc, argv); + std::unique_ptr context = xeus::make_zmq_context(); + nl::json debugger_config; debugger_config["lldb"]["initCommands"] = { "settings set plugin.jit-loader.gdb.enable on" }; -#endif - - std::string file_name = xeus::extract_filename(argc, argv); - auto interpreter = std::make_unique(argc, argv); - std::unique_ptr context = xeus::make_zmq_context(); + debugger_config["interpreter_ptr"] = reinterpret_cast(interpreter.get()); if (!file_name.empty()) { @@ -86,13 +85,9 @@ int main(int argc, char* argv[]) xeus::make_console_logger( xeus::xlogger::msg_type, xeus::make_file_logger(xeus::xlogger::content, "xeus.log") -#ifdef XEUS_CPP_USE_DEBUGGER ), xcpp::make_cpp_debugger, debugger_config -#else - ) -#endif ); std::clog << "Starting xcpp kernel...\n\n" @@ -113,13 +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") -#ifdef XEUS_CPP_USE_DEBUGGER ), xcpp::make_cpp_debugger, debugger_config -#else - ) -#endif ); std::cout << "Getting config" << std::endl; diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 22fe0476..7138ca77 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -22,6 +22,15 @@ using namespace std::placeholders; +// ***** ONLY FOR DEBUGGING PURPOSES. NOT TO BE COMMITTED ***** +static std::ofstream log_stream("/Users/abhinavkumar/Desktop/Coding/CERN_HSF_COMPILER_RESEARCH/xeus-cpp/build/xeus-cpp-logs.log", std::ios::app); + +void log_debug(const std::string& msg) { + log_stream << "[xeus-cpp] " << msg << std::endl; + log_stream.flush(); // Ensure immediate write +} +// ******* (END) ONLY FOR DEBUGGING PURPOSES. NOT TO BE COMMITTED ******* + namespace xcpp { debugger::debugger( @@ -57,6 +66,12 @@ namespace xcpp std::bind(&debugger::configuration_done_request, this, _1), true ); + register_request_handler("setBreakpoints", std::bind(&debugger::set_breakpoints_request, this, _1), true); + register_request_handler("dumpCell", std::bind(&debugger::dump_cell_request, this, _1), true); + + m_interpreter = reinterpret_cast( + debugger_config["interpreter_ptr"].get() + ); } debugger::~debugger() @@ -90,9 +105,12 @@ namespace xcpp xeus::create_directory(log_dir); std::string log_file = log_dir + "/lldb-dap.log"; + // std::cout << "Log file for LLDB-DAP: " << log_file << std::endl; + pid_t pid = fork(); if (pid == 0) { + // ***** ONLY FOR DEBUGGING PURPOSES. NOT TO BE COMMITTED ***** int fd = open(log_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd != -1) { @@ -100,6 +118,7 @@ namespace xcpp dup2(fd, STDERR_FILENO); close(fd); } + // ******* (END) ONLY FOR DEBUGGING PURPOSES. NOT TO BE COMMITTED ****** int null_fd = open("/dev/null", O_RDONLY); if (null_fd != -1) @@ -174,36 +193,49 @@ namespace xcpp nl::json debugger::attach_request(const nl::json& message) { // Placeholder DAP response - nl::json attach_request = - {{"seq", 2}, {"type", "request"}, {"command", "attach"}, {"arguments", {{"pid", jit_process_pid}}}}; - std::cout << "Sending attach request: " << attach_request.dump() << std::endl; + nl::json attach_request = { + {"seq", message["seq"]}, + {"type", "request"}, + {"command", "attach"}, + {"arguments", + {{"pid", jit_process_pid}, {"initCommands", {"settings set plugin.jit-loader.gdb.enable on"}}}} + }; nl::json reply = forward_message(attach_request); return reply; } nl::json debugger::inspect_variables_request(const nl::json& message) { - std::cout << "[debugger::inspect_variables_request] inspect_variables_request not implemented" - << std::endl; - nl::json reply = { - {"type", "response"}, - {"request_seq", message["seq"]}, - {"success", true}, - {"command", message["command"]}, - {"body", - {{"variables", - {{{"name", "a"}, {"value", "100"}, {"type", "int"}, {"evaluateName", "a"}, {"variablesReference", 0}}, - {{"name", "b"}, - {"value", "1000"}, - {"type", "int"}, - {"evaluateName", "b"}, - {"variablesReference", 0}}}}}} + log_debug("[debugger] Inspect variable not implemented."); + nl::json inspect_request = { + {"seq", message["seq"]}, + {"type", "request"}, + {"command", "variables"}, + {"arguments", {{"variablesReference", 0}}} }; + nl::json reply = forward_message(inspect_request); return reply; } + nl::json debugger::set_breakpoints_request(const nl::json& message) + { + std::string source = message["arguments"]["source"]["path"].get(); + m_breakpoint_list.erase(source); + nl::json bp_json = message["arguments"]["breakpoints"]; + std::vector bp_list(bp_json.begin(), bp_json.end()); + std::string sequential_source = m_hash_to_sequential[source][0]; + m_breakpoint_list.insert(std::make_pair(std::move(source), std::move(bp_list))); + nl::json mod_message = message; + mod_message["arguments"]["source"]["path"] = sequential_source; + log_debug("Set breakpoints request received:\n" + mod_message.dump(4)); + nl::json breakpoint_reply = forward_message(mod_message); + log_debug("Set breakpoints reply sent:\n" + breakpoint_reply.dump(4)); + return breakpoint_reply; + } + nl::json debugger::stack_trace_request(const nl::json& message) { + log_debug("Stack trace request received:\n" + message.dump(4)); nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -217,17 +249,15 @@ namespace xcpp nl::json debugger::configuration_done_request(const nl::json& message) { - nl::json reply = { - {"type", "response"}, - {"request_seq", message["seq"]}, - {"success", true}, - {"command", message["command"]} - }; + log_debug("Configuration done request received:\n" + message.dump(4)); + nl::json reply = forward_message(message); + log_debug("Configuration done reply sent:\n" + reply.dump(4)); return reply; } nl::json debugger::variables_request_impl(const nl::json& message) { + log_debug("Variables request received:\n" + message.dump(4)); nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -259,14 +289,52 @@ namespace xcpp ); } + nl::json debugger::dump_cell_request(const nl::json& message) + { + std::string code; + try + { + code = message["arguments"]["code"].get(); + // std::cout << "Execution Count is: " << m_interpreter->get_execution_count(code)[0] << std::endl; + } + catch (nl::json::type_error& e) + { + std::clog << e.what() << std::endl; + } + catch (...) + { + std::clog << "XDEBUGGER: Unknown issue" << std::endl; + } + std::string hash_file_name = get_cell_temporary_file(code); + for(auto& exec_count: m_interpreter->get_execution_count(code)) + { + m_hash_to_sequential[hash_file_name].push_back("input_line_" + std::to_string(exec_count)); + m_sequential_to_hash["input_line_" + std::to_string(exec_count)] = hash_file_name; + } + + // std::string next_file_name = "input_line_" + std::to_string(m_interpreter->get_execution_count(code).size() ? m_interpreter->get_execution_count(code)[0] : 0); + + std::fstream fs(hash_file_name, std::ios::in); + if (!fs.is_open()) + { + fs.clear(); + fs.open(hash_file_name, std::ios::out); + fs << code; + } + + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", true}, + {"command", message["command"]}, + {"body", {{"sourcePath", hash_file_name}}} + }; + return reply; + } + 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; + return get_cell_tmp_file(code); } std::unique_ptr make_cpp_debugger( diff --git a/src/xinternal_utils.cpp b/src/xinternal_utils.cpp index 20974898..ae00b332 100644 --- a/src/xinternal_utils.cpp +++ b/src/xinternal_utils.cpp @@ -53,13 +53,6 @@ namespace xcpp #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"); diff --git a/src/xinternal_utils.hpp b/src/xinternal_utils.hpp index bf084172..0d5a67d9 100644 --- a/src/xinternal_utils.hpp +++ b/src/xinternal_utils.hpp @@ -12,9 +12,6 @@ namespace xcpp 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(); diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index c55c2ed8..f4c7ad11 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -51,17 +51,12 @@ void* createInterpreter(const Args& ExtraArgs = {}) // FIXME: We should process the kernel input options and conditionally pass // the gpu args here. if (std::find_if(ExtraArgs.begin(), ExtraArgs.end(), [](const std::string& s) { - return s == "-gdwarf-4";}) == ExtraArgs.end()) { + return s == "-g";}) == ExtraArgs.end()) { // If no debugger option, then use the non-OOP JIT execution. return Cpp::CreateInterpreter(ClangArgs /*, {"-cuda"}*/); } -#ifdef XEUS_CPP_USE_DEBUGGER -// If debugger option is set, then use the OOP JIT execution. return Cpp::CreateInterpreter(ClangArgs, {}, true); -#else - return Cpp::CreateInterpreter(ClangArgs); -#endif } using namespace std::placeholders; @@ -86,12 +81,10 @@ namespace xcpp xeus::register_interpreter(this); } -#ifdef XEUS_CPP_USE_DEBUGGER pid_t interpreter::get_current_pid() { return Cpp::GetExecutorPID(); } -#endif static std::string get_stdopt() { @@ -129,11 +122,7 @@ __get_cxx_version () { // NOLINTNEXTLINE (cppcoreguidelines-pro-bounds-pointer-arithmetic) createInterpreter(Args(argv ? argv + 1 : argv, argv + argc)); -#ifdef XEUS_CPP_USE_DEBUGGER // m_version = get_stdopt(); -#else - m_version = get_stdopt(); -#endif redirect_output(); init_preamble(); init_magic(); @@ -146,7 +135,7 @@ __get_cxx_version () void interpreter::execute_request_impl( send_reply_callback cb, - int /*execution_count*/, + int execution_count, const std::string& code, xeus::execute_request_config config, nl::json /*user_expressions*/ @@ -188,6 +177,8 @@ __get_cxx_version () std::string err; + m_code_to_execution_count_map[code].push_back(execution_count); + // Attempt normal evaluation try { @@ -359,112 +350,6 @@ __get_cxx_version () restore_output(); } - nl::json interpreter::internal_request_impl(const nl::json& content) - { - std::string code = content.value("code", ""); - nl::json reply; - 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 0cba670c6ec53160c116f4106b903e05c89c0060 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Tue, 24 Jun 2025 17:00:01 +0530 Subject: [PATCH 16/19] debugging breakpoint hits --- include/xeus-cpp/xdebugger.hpp | 4 + src/xdebugger.cpp | 278 ++++++++++++++++++++++++++++++--- src/xdebuglldb_client.cpp | 16 ++ 3 files changed, 277 insertions(+), 21 deletions(-) diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp index 240e61d9..c9978411 100644 --- a/include/xeus-cpp/xdebugger.hpp +++ b/include/xeus-cpp/xdebugger.hpp @@ -51,6 +51,8 @@ namespace xcpp nl::json configuration_done_request(const nl::json& message); nl::json set_breakpoints_request(const nl::json& message); nl::json dump_cell_request(const nl::json& message); + nl::json rich_inspect_variables_request(const nl::json& message); + nl::json copy_to_globals_request(const nl::json& message); nl::json variables_request_impl(const nl::json& message) override; @@ -80,6 +82,8 @@ namespace xcpp std::unordered_map> m_hash_to_sequential; std::unordered_map m_sequential_to_hash; + + bool m_copy_to_globals_available; }; XEUS_CPP_API diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 7138ca77..18047481 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -51,7 +51,6 @@ namespace xcpp , 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( @@ -59,15 +58,17 @@ namespace xcpp 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 ); + register_request_handler("richInspectVariables", std::bind(&debugger::rich_inspect_variables_request, this, _1), true); register_request_handler("setBreakpoints", std::bind(&debugger::set_breakpoints_request, this, _1), true); register_request_handler("dumpCell", std::bind(&debugger::dump_cell_request, this, _1), true); + register_request_handler("copyToGlobals", std::bind(&debugger::copy_to_globals_request, this, _1), true); + register_request_handler("stackTrace", std::bind(&debugger::stack_trace_request, this, _1), true); m_interpreter = reinterpret_cast( debugger_config["interpreter_ptr"].get() @@ -98,7 +99,12 @@ namespace xcpp log << m_debugger_config.dump(4) << '\n'; } - std::vector lldb_args = {"lldb-dap", "--port", m_lldb_port}; + // std::vector lldb_args = {"/Users/abhinavkumar/Desktop/Coding/CERN_HSF_COMPILER_RESEARCH/llvm-project-lldb/build/bin/lldb-dap", "--connection", "listen://localhost:" + m_lldb_port}; + std::vector lldb_args = { + "lldb-dap", + "--port", + m_lldb_port + }; std::string log_dir = xeus::get_temp_directory_path() + "/xcpp_debug_logs_" + std::to_string(xeus::get_current_pid()); @@ -148,6 +154,7 @@ namespace xcpp return false; } m_is_running = true; + m_copy_to_globals_available = true; return true; } else @@ -173,7 +180,7 @@ namespace xcpp std::string lldb_endpoint = "tcp://" + m_lldb_host + ":" + m_lldb_port; std::thread client( - &xdebuglldb_client::start_debugger, + &xdap_tcp_client::start_debugger, p_debuglldb_client, lldb_endpoint, publisher_end_point, @@ -198,15 +205,21 @@ namespace xcpp {"type", "request"}, {"command", "attach"}, {"arguments", - {{"pid", jit_process_pid}, {"initCommands", {"settings set plugin.jit-loader.gdb.enable on"}}}} + { + {"pid", jit_process_pid}, + {"stopOnEntry", true}, + {"initCommands", {"settings set plugin.jit-loader.gdb.enable on"}} + } + } }; + log_debug("[debugger::attach_request] Attach request sent:\n" + attach_request.dump(4)); nl::json reply = forward_message(attach_request); return reply; } nl::json debugger::inspect_variables_request(const nl::json& message) { - log_debug("[debugger] Inspect variable not implemented."); + log_debug("[debugger::inspect_variables_request] Inspect variable not implemented."); nl::json inspect_request = { {"seq", message["seq"]}, {"type", "request"}, @@ -227,37 +240,99 @@ namespace xcpp m_breakpoint_list.insert(std::make_pair(std::move(source), std::move(bp_list))); nl::json mod_message = message; mod_message["arguments"]["source"]["path"] = sequential_source; - log_debug("Set breakpoints request received:\n" + mod_message.dump(4)); + log_debug("[debugger::set_breakpoints_request] Set breakpoints request sent:\n" + mod_message.dump(4)); nl::json breakpoint_reply = forward_message(mod_message); - log_debug("Set breakpoints reply sent:\n" + breakpoint_reply.dump(4)); + if (breakpoint_reply.contains("body") && breakpoint_reply["body"].contains("breakpoints")) + { + for (auto& bp : breakpoint_reply["body"]["breakpoints"]) + { + if (bp.contains("source") && bp["source"].contains("path")) + { + std::string seq_path = bp["source"]["path"].get(); + if (m_sequential_to_hash.find(seq_path) != m_sequential_to_hash.end()) + { + bp["source"]["path"] = m_sequential_to_hash[seq_path]; + } + } + } + } + log_debug("[debugger::set_breakpoints_request] Set breakpoints reply received:\n" + breakpoint_reply.dump(4)); return breakpoint_reply; } - nl::json debugger::stack_trace_request(const nl::json& message) + // nl::json debugger::stack_trace_request(const nl::json& message) + // { + // log_debug("Stack trace request received:\n" + message.dump(4)); + // 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::copy_to_globals_request(const nl::json& message) { - log_debug("Stack trace request received:\n" + message.dump(4)); - nl::json reply = { - {"type", "response"}, - {"request_seq", message["seq"]}, - {"success", false}, - {"command", message["command"]}, - {"message", "stackTrace not implemented"}, - {"body", {{"stackFrames", {}}}} + // This request cannot be processed if the version of debugpy is lower than 1.6.5. + log_debug("[debugger::copy_to_globals_request] Copy to globals request received:\n" + message.dump(4)); + if (!m_copy_to_globals_available) + { + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]}, + {"body", "The debugpy version must be greater than or equal 1.6.5 to allow copying a variable to the global scope."} + }; + return reply; + } + + std::string src_var_name = message["arguments"]["srcVariableName"].get(); + std::string dst_var_name = message["arguments"]["dstVariableName"].get(); + int src_frame_id = message["arguments"]["srcFrameId"].get(); + + // It basically runs a setExpression in the globals dictionary of Python. + int seq = message["seq"].get(); + std::string expression = "globals()['" + dst_var_name + "']"; + nl::json request = { + {"type", "request"}, + {"command", "setExpression"}, + {"seq", seq+1}, + {"arguments", { + {"expression", expression}, + {"value", src_var_name}, + {"frameId", src_frame_id} + }} }; - return reply; + return forward_message(request); } + nl::json debugger::configuration_done_request(const nl::json& message) { - log_debug("Configuration done request received:\n" + message.dump(4)); + log_debug("[debugger::configuration_done_request] Configuration done request received:\n" + message.dump(4)); nl::json reply = forward_message(message); - log_debug("Configuration done reply sent:\n" + reply.dump(4)); + log_debug("[debugger::configuration_done_request] Configuration done reply sent:\n" + reply.dump(4)); + // send continue reauest on thread that is stopped + auto stopped_threads = base_type::get_stopped_threads(); + nl::json continue_request = { + {"type", "request"}, + {"command", "continue"}, + {"seq", message["seq"].get() + 1}, + {"arguments", {{"threadId", *stopped_threads.begin()}}} + }; + log_debug("[debugger::configuration_done_request] Sending continue request:\n" + continue_request.dump(4)); + nl::json continue_reply = forward_message(continue_request); + log_debug("[debugger::configuration_done_request] Continue reply received:\n" + continue_reply.dump(4)); return reply; } nl::json debugger::variables_request_impl(const nl::json& message) { - log_debug("Variables request received:\n" + message.dump(4)); + log_debug("[debugger::variables_request_impl] Variables request received:\n" + message.dump(4)); nl::json reply = { {"type", "response"}, {"request_seq", message["seq"]}, @@ -269,6 +344,165 @@ namespace xcpp return reply; } + nl::json debugger::rich_inspect_variables_request(const nl::json& message) + { + log_debug("[debugger::rich_inspect_variables_request] Rich inspect variables request received:\n" + message.dump(4)); + return {}; + // nl::json reply = { + // {"type", "response"}, + // {"request_seq", message["seq"]}, + // {"success", false}, + // {"command", message["command"]} + // }; + + // std::string var_name = message["arguments"]["variableName"].get(); + // py::str py_var_name = py::str(var_name); + // bool valid_name = PyUnicode_IsIdentifier(py_var_name.ptr()) == 1; + // if (!valid_name) + // { + // reply["body"] = { + // {"data", {}}, + // {"metadata", {}} + // }; + // if (var_name == "special variables" || var_name == "function variables") + // { + // reply["success"] = true; + // } + // return reply; + // } + + // std::string var_repr_data = var_name + "_repr_data"; + // std::string var_repr_metadata = var_name + "_repr_metada"; + + // if (base_type::get_stopped_threads().empty()) + // { + // // The code did not hit a breakpoint, we use the interpreter + // // to get the rich representation of the variable + // std::string code = "from IPython import get_ipython;"; + // code += var_repr_data + ',' + var_repr_metadata + "= get_ipython().display_formatter.format(" + var_name + ")"; + // py::gil_scoped_acquire acquire; + // exec(py::str(code)); + // } + // else + // { + // // The code has stopped on a breakpoint, we use the setExpression request + // // to get the rich representation of the variable + // std::string code = "get_ipython().display_formatter.format(" + var_name + ")"; + // int frame_id = message["arguments"]["frameId"].get(); + // int seq = message["seq"].get(); + // nl::json request = { + // {"type", "request"}, + // {"command", "evaluate"}, + // {"seq", seq+1}, + // {"arguments", { + // {"expression", code}, + // {"frameId", frame_id}, + // {"context", "clipboard"} + // }} + // }; + // nl::json request_reply = forward_message(request); + // std::string result = request_reply["body"]["result"]; + + // py::gil_scoped_acquire acquire; + // std::string exec_code = var_repr_data + ',' + var_repr_metadata + "= eval(str(" + result + "))"; + // exec(py::str(exec_code)); + // } + + // py::gil_scoped_acquire acquire; + // py::object variables = py::globals(); + // py::object repr_data = variables[py::str(var_repr_data)]; + // py::object repr_metadata = variables[py::str(var_repr_metadata)]; + // nl::json body = { + // {"data", {}}, + // {"metadata", {}} + // }; + // for (const py::handle& key : repr_data) + // { + // std::string data_key = py::str(key); + // body["data"][data_key] = repr_data[key]; + // if (repr_metadata.contains(key)) + // { + // body["metadata"][data_key] = repr_metadata[key]; + // } + // } + // PyDict_DelItem(variables.ptr(), py::str(var_repr_data).ptr()); + // PyDict_DelItem(variables.ptr(), py::str(var_repr_metadata).ptr()); + // reply["body"] = body; + // reply["success"] = true; + // return reply; + } + + nl::json debugger::stack_trace_request(const nl::json& message) + { + int requested_thread_id = message["arguments"]["threadId"]; + + auto stopped_threads = base_type::get_stopped_threads(); + + if (stopped_threads.empty()) { + nl::json error_reply = { + {"command", "stackTrace"}, + {"request_seq", message["seq"]}, + {"seq", message["seq"]}, + {"success", false}, + {"message", "No threads are currently stopped"}, + {"type", "response"} + }; + return error_reply; + } + + int actual_thread_id; + nl::json modified_message = message; + + if (requested_thread_id == 1 && !stopped_threads.empty()) { + actual_thread_id = *stopped_threads.begin(); + modified_message["arguments"]["threadId"] = actual_thread_id; + std::clog << "XDEBUGGER: Mapping client thread 1 to actual thread " << actual_thread_id << std::endl; + } + else if (stopped_threads.find(requested_thread_id) != stopped_threads.end()) { + actual_thread_id = requested_thread_id; + } + else { + actual_thread_id = *stopped_threads.begin(); + modified_message["arguments"]["threadId"] = actual_thread_id; + std::clog << "XDEBUGGER: Thread " << requested_thread_id + << " not found in stopped threads, using " << actual_thread_id << std::endl; + } + + for(auto x : stopped_threads) + { + log_debug("Stopped thread ID: " + std::to_string(x)); + } + + log_debug("[debugger::stack_trace_request]:\n" + modified_message.dump(4)); + + nl::json reply = forward_message(modified_message); + + if (!reply.contains("body") || !reply["body"].contains("stackFrames")) { + return reply; + } + + auto& stack_frames = reply["body"]["stackFrames"]; + for (auto it = stack_frames.begin(); it != stack_frames.end(); ++it) { + if (it->contains("source") && (*it)["source"].contains("path") && + (*it)["source"]["path"] == "") { + stack_frames.erase(it); + break; + } + } + + #ifdef WIN32 + for (auto& frame : stack_frames) { + if (frame.contains("source") && frame["source"].contains("path")) { + std::string path = frame["source"]["path"]; + std::replace(path.begin(), path.end(), '\\', '/'); + frame["source"]["path"] = path; + } + } + #endif + + return reply; + } + void debugger::stop() { std::string controller_end_point = xeus::get_controller_end_point("debugger"); @@ -279,6 +513,7 @@ namespace xcpp xeus::xdebugger_info debugger::get_debugger_info() const { // Placeholder debugger info + log_debug("[debugger::get_debugger_info] Returning debugger info"); return xeus::xdebugger_info( xeus::get_tmp_hash_seed(), get_tmp_prefix(), @@ -291,6 +526,7 @@ namespace xcpp nl::json debugger::dump_cell_request(const nl::json& message) { + log_debug("[debugger::dump_cell_request] Dump cell request received:\n" + message.dump(4)); std::string code; try { diff --git a/src/xdebuglldb_client.cpp b/src/xdebuglldb_client.cpp index e914ac5b..6b446181 100644 --- a/src/xdebuglldb_client.cpp +++ b/src/xdebuglldb_client.cpp @@ -11,9 +11,22 @@ #include "nlohmann/json.hpp" #include "xeus/xmessage.hpp" #include +#include namespace nl = nlohmann; +// ***** ONLY FOR DEBUGGING PURPOSES. NOT TO BE COMMITTED ***** +static std::ofstream log_stream_client("/Users/abhinavkumar/Desktop/Coding/CERN_HSF_COMPILER_RESEARCH/xeus-cpp/build/xeus-cpp-xdebuglldb_client-logs.log", std::ios::app); + +void log_debug_client(const std::string& msg) { + log_stream_client << "[xeus-cpp-debuglldb_client] " << msg << std::endl; + log_stream_client.flush(); // Ensure immediate write +} +// ******* (END) ONLY FOR DEBUGGING PURPOSES. NOT TO BE COMMITTED ******* + +void log_debug(const std::string& msg); + + namespace xcpp { xdebuglldb_client::xdebuglldb_client( @@ -32,12 +45,15 @@ namespace xcpp void xdebuglldb_client::handle_event(nl::json message) { // Forward DAP events to the base class (e.g., "stopped" events from LLDB-DAP) + log_debug_client("[xdebuglldb_client::handle_event] Handling event:\n" + message.dump(4)); forward_event(std::move(message)); } nl::json xdebuglldb_client::get_stack_frames(int thread_id, int seq) { // Construct a DAP stackTrace request + log_debug_client("Requesting stack frames for thread ID: " + std::to_string(thread_id) + " with sequence number: " + std::to_string(seq)); + // std::cout << "Requesting stack frames for thread ID: " << thread_id << " with sequence number: " << seq << std::endl; nl::json request = { {"type", "request"}, {"seq", seq}, From eda00f8747c455a54df3e46664a5897b4128591f Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Wed, 25 Jun 2025 14:56:13 +0530 Subject: [PATCH 17/19] debugging breakpoint hits --- src/xdebugger.cpp | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index 18047481..fc10d5a1 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -207,7 +207,6 @@ namespace xcpp {"arguments", { {"pid", jit_process_pid}, - {"stopOnEntry", true}, {"initCommands", {"settings set plugin.jit-loader.gdb.enable on"}} } } @@ -226,8 +225,8 @@ namespace xcpp {"command", "variables"}, {"arguments", {{"variablesReference", 0}}} }; - nl::json reply = forward_message(inspect_request); - return reply; + nl::json dummy_reply = forward_message(inspect_request); + return dummy_reply; } nl::json debugger::set_breakpoints_request(const nl::json& message) @@ -321,10 +320,12 @@ namespace xcpp nl::json continue_request = { {"type", "request"}, {"command", "continue"}, - {"seq", message["seq"].get() + 1}, + {"seq", message["seq"].get() + 2}, {"arguments", {{"threadId", *stopped_threads.begin()}}} }; log_debug("[debugger::configuration_done_request] Sending continue request:\n" + continue_request.dump(4)); + // // wait for 3 seconds before sending continue request + // std::this_thread::sleep_for(std::chrono::seconds(3)); nl::json continue_reply = forward_message(continue_request); log_debug("[debugger::configuration_done_request] Continue reply received:\n" + continue_reply.dump(4)); return reply; @@ -333,14 +334,7 @@ namespace xcpp nl::json debugger::variables_request_impl(const nl::json& message) { log_debug("[debugger::variables_request_impl] Variables request received:\n" + message.dump(4)); - nl::json reply = { - {"type", "response"}, - {"request_seq", message["seq"]}, - {"success", false}, - {"command", message["command"]}, - {"message", "variables not implemented"}, - {"body", {{"variables", {}}}} - }; + nl::json reply = forward_message(message); return reply; } @@ -439,15 +433,8 @@ namespace xcpp auto stopped_threads = base_type::get_stopped_threads(); if (stopped_threads.empty()) { - nl::json error_reply = { - {"command", "stackTrace"}, - {"request_seq", message["seq"]}, - {"seq", message["seq"]}, - {"success", false}, - {"message", "No threads are currently stopped"}, - {"type", "response"} - }; - return error_reply; + nl::json reply = forward_message(message); + return reply; } int actual_thread_id; From 290becdb2e59bea6fdb2892187fafa789218e56c Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Mon, 30 Jun 2025 19:47:17 +0530 Subject: [PATCH 18/19] added variable inspection --- include/xeus-cpp/xdebugger.hpp | 6 +- include/xeus-cpp/xinterpreter.hpp | 13 + src/main.cpp | 5 +- src/xdebugger.cpp | 502 ++++++++++++++++++++++-------- src/xinterpreter.cpp | 1 + 5 files changed, 392 insertions(+), 135 deletions(-) diff --git a/include/xeus-cpp/xdebugger.hpp b/include/xeus-cpp/xdebugger.hpp index c9978411..4408c46f 100644 --- a/include/xeus-cpp/xdebugger.hpp +++ b/include/xeus-cpp/xdebugger.hpp @@ -53,8 +53,12 @@ namespace xcpp nl::json dump_cell_request(const nl::json& message); nl::json rich_inspect_variables_request(const nl::json& message); nl::json copy_to_globals_request(const nl::json& message); + nl::json source_request(const nl::json& message); - nl::json variables_request_impl(const nl::json& message) override; + bool get_variable_info_from_lldb(const std::string& var_name, int frame_id, nl::json& data, nl::json& metadata, int sequence); + void get_container_info(const std::string& var_name, int frame_id, + const std::string& var_type, nl::json& data, nl::json& metadata, int sequence, int size); + bool is_container_type(const std::string& type) const; bool start_lldb(); bool start() override; diff --git a/include/xeus-cpp/xinterpreter.hpp b/include/xeus-cpp/xinterpreter.hpp index 1577751d..7fe49ac3 100644 --- a/include/xeus-cpp/xinterpreter.hpp +++ b/include/xeus-cpp/xinterpreter.hpp @@ -25,6 +25,8 @@ #include "xbuffer.hpp" #include "xeus_cpp_config.hpp" #include "xmanager.hpp" +#include + namespace nl = nlohmann; @@ -52,6 +54,16 @@ namespace xcpp return {}; } + std::string get_code_from_execution_count(int execution_count) const + { + auto it = m_execution_count_to_code_map.find(execution_count); + if(it != m_execution_count_to_code_map.end()) + { + return it->second; + } + return ""; + } + private: void configure_impl() override; @@ -99,6 +111,7 @@ namespace xcpp xoutput_buffer m_cerr_buffer; std::map> m_code_to_execution_count_map; + std::map m_execution_count_to_code_map; }; } diff --git a/src/main.cpp b/src/main.cpp index 1dcfaf7d..1a236dc1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,7 @@ #include #include "xeus-zmq/xzmq_context.hpp" #include +#include "xeus-zmq/xserver_zmq_split.hpp" #include "xeus-cpp/xeus_cpp_config.hpp" #include "xeus-cpp/xinterpreter.hpp" #include "xeus-cpp/xutils.hpp" @@ -80,7 +81,7 @@ int main(int argc, char* argv[]) xeus::get_user_name(), std::move(context), std::move(interpreter), - xeus::make_xserver_default, + xeus::make_xserver_control_main, xeus::make_in_memory_history_manager(), xeus::make_console_logger( xeus::xlogger::msg_type, @@ -103,7 +104,7 @@ int main(int argc, char* argv[]) xeus::get_user_name(), std::move(context), std::move(interpreter), - xeus::make_xserver_default, + xeus::make_xserver_control_main, xeus::make_in_memory_history_manager(), xeus::make_console_logger( xeus::xlogger::msg_type, diff --git a/src/xdebugger.cpp b/src/xdebugger.cpp index fc10d5a1..89000276 100644 --- a/src/xdebugger.cpp +++ b/src/xdebugger.cpp @@ -69,6 +69,7 @@ namespace xcpp register_request_handler("dumpCell", std::bind(&debugger::dump_cell_request, this, _1), true); register_request_handler("copyToGlobals", std::bind(&debugger::copy_to_globals_request, this, _1), true); register_request_handler("stackTrace", std::bind(&debugger::stack_trace_request, this, _1), true); + register_request_handler("source", std::bind(&debugger::source_request, this, _1), true); m_interpreter = reinterpret_cast( debugger_config["interpreter_ptr"].get() @@ -166,13 +167,13 @@ namespace xcpp bool debugger::start() { - static bool lldb_started = start_lldb(); + bool lldb_started = start_lldb(); if (!lldb_started) { std::cerr << "Failed to start LLDB-DAP" << std::endl; return false; } - + 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(); @@ -259,20 +260,6 @@ namespace xcpp return breakpoint_reply; } - // nl::json debugger::stack_trace_request(const nl::json& message) - // { - // log_debug("Stack trace request received:\n" + message.dump(4)); - // 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::copy_to_globals_request(const nl::json& message) { // This request cannot be processed if the version of debugpy is lower than 1.6.5. @@ -313,117 +300,276 @@ namespace xcpp nl::json debugger::configuration_done_request(const nl::json& message) { log_debug("[debugger::configuration_done_request] Configuration done request received:\n" + message.dump(4)); + if(!base_type::get_stopped_threads().empty()) { + // Return a dummy reply as per user request. + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", true}, + {"command", message["command"]}, + {"body", {{"info", "Dummy reply: continue request not sent as requested."}}} + }; + log_debug("[debugger::configuration_done_request] Returning dummy reply instead of sending continue request."); + return reply; + } nl::json reply = forward_message(message); log_debug("[debugger::configuration_done_request] Configuration done reply sent:\n" + reply.dump(4)); - // send continue reauest on thread that is stopped - auto stopped_threads = base_type::get_stopped_threads(); - nl::json continue_request = { - {"type", "request"}, - {"command", "continue"}, - {"seq", message["seq"].get() + 2}, - {"arguments", {{"threadId", *stopped_threads.begin()}}} - }; - log_debug("[debugger::configuration_done_request] Sending continue request:\n" + continue_request.dump(4)); - // // wait for 3 seconds before sending continue request - // std::this_thread::sleep_for(std::chrono::seconds(3)); - nl::json continue_reply = forward_message(continue_request); - log_debug("[debugger::configuration_done_request] Continue reply received:\n" + continue_reply.dump(4)); return reply; } - nl::json debugger::variables_request_impl(const nl::json& message) + void debugger::get_container_info(const std::string& var_name, int frame_id, + const std::string& var_type, nl::json& data, nl::json& metadata, int sequence, int size) { - log_debug("[debugger::variables_request_impl] Variables request received:\n" + message.dump(4)); - nl::json reply = forward_message(message); - return reply; + // Create a tree structure for the container + nl::json container_tree = { + {"type", "container"}, + {"name", var_name}, + {"containerType", var_type}, + {"size", size}, + {"children", nl::json::array()}, + {"expanded", false} + }; + + // Add elements as children + for (int i = 0; i < size; ++i) { + nl::json elem_request = { + {"type", "request"}, + {"command", "evaluate"}, + {"seq", sequence++}, + {"arguments", { + {"expression", var_name + "[" + std::to_string(i) + "]"}, + {"frameId", frame_id}, + {"context", "watch"} + }} + }; + + nl::json elem_reply = forward_message(elem_request); + if (elem_reply["success"].get()) { + std::string elem_value = elem_reply["body"]["result"].get(); + std::string elem_type = elem_reply["body"].contains("type") ? + elem_reply["body"]["type"].get() : "unknown"; + + nl::json child_node = { + {"type", "element"}, + {"name", "[" + std::to_string(i) + "]"}, + {"value", elem_value}, + {"valueType", elem_type}, + {"index", i}, + {"leaf", true} + }; + + // Check if element is also a container + if (is_container_type(elem_type)) { + child_node["leaf"] = false; + child_node["hasChildren"] = true; + // Size extraction for nested containers + int nested_size = 0; + std::smatch match; + std::regex size_regex(R"(size\s*=\s*(\d+))"); + if (std::regex_search(elem_value, match, size_regex) && match.size() > 1) { + nested_size = std::stoi(match[1]); + } + child_node["size"] = nested_size; + } + + container_tree["children"].push_back(child_node); + } + } + + // Store the tree structure in JSON format + data["application/json"] = container_tree; + } + + bool debugger::is_container_type(const std::string& type) const + { + return type.find("std::vector") != std::string::npos || + type.find("std::array") != std::string::npos || + type.find("std::list") != std::string::npos || + type.find("std::deque") != std::string::npos || + type.find("std::map") != std::string::npos || + type.find("std::set") != std::string::npos || + type.find("std::unordered_map") != std::string::npos || + type.find("std::unordered_set") != std::string::npos; + } + + bool debugger::get_variable_info_from_lldb(const std::string& var_name, int frame_id, nl::json& data, nl::json& metadata, int sequence) { + try { + nl::json eval_request = { + {"type", "request"}, + {"command", "evaluate"}, + {"seq", sequence}, + {"arguments", { + {"expression", var_name}, + {"frameId", frame_id}, + {"context", "watch"} + }} + }; + + nl::json eval_reply = forward_message(eval_request); + if (!eval_reply["success"].get()) { + return false; + } + + std::string var_value = eval_reply["body"]["result"].get(); + std::string var_type = eval_reply["body"].contains("type") ? + eval_reply["body"]["type"].get() : "unknown"; + + // Create a unified JSON structure for all variable types + nl::json variable_info = { + {"type", "variable"}, + {"name", var_name}, + {"value", var_value}, + {"valueType", var_type}, + {"frameId", frame_id}, + {"timestamp", std::time(nullptr)} + }; + + if(is_container_type(var_type)) { + // Extract size from var_value, e.g., "size=2" + int size = 0; + std::smatch match; + std::regex size_regex(R"(size\s*=\s*(\d+))"); + if (std::regex_search(var_value, match, size_regex) && match.size() > 1) { + size = std::stoi(match[1]); + } + + variable_info["isContainer"] = true; + variable_info["size"] = size; + variable_info["leaf"] = false; + variable_info["hasChildren"] = size > 0; + + // Get detailed container info + get_container_info(var_name, frame_id, var_type, data, metadata, sequence + 1, size); + + // Add container summary to the main variable info + if (data["application/json"].contains("children")) { + variable_info["children"] = data["application/json"]["children"]; + variable_info["expanded"] = false; + } + } else { + variable_info["isContainer"] = false; + variable_info["leaf"] = true; + variable_info["hasChildren"] = false; + } + + // Store everything in application/json format + data["application/json"] = variable_info; + + // Metadata for the frontend renderer + metadata["application/json"] = { + {"version", "1.0"}, + {"renderer", "variable-inspector"}, + {"expandable", variable_info["hasChildren"]}, + {"rootVariable", var_name}, + {"capabilities", { + {"tree", true}, + {"search", true}, + {"filter", true}, + {"export", true} + }} + }; + + return true; + } catch (const std::exception& e) { + log_debug("[get_variable_info_from_lldb] Exception: " + std::string(e.what())); + return false; + } } nl::json debugger::rich_inspect_variables_request(const nl::json& message) { log_debug("[debugger::rich_inspect_variables_request] Rich inspect variables request received:\n" + message.dump(4)); - return {}; - // nl::json reply = { - // {"type", "response"}, - // {"request_seq", message["seq"]}, - // {"success", false}, - // {"command", message["command"]} - // }; - - // std::string var_name = message["arguments"]["variableName"].get(); - // py::str py_var_name = py::str(var_name); - // bool valid_name = PyUnicode_IsIdentifier(py_var_name.ptr()) == 1; - // if (!valid_name) - // { - // reply["body"] = { - // {"data", {}}, - // {"metadata", {}} - // }; - // if (var_name == "special variables" || var_name == "function variables") - // { - // reply["success"] = true; - // } - // return reply; - // } - - // std::string var_repr_data = var_name + "_repr_data"; - // std::string var_repr_metadata = var_name + "_repr_metada"; - - // if (base_type::get_stopped_threads().empty()) - // { - // // The code did not hit a breakpoint, we use the interpreter - // // to get the rich representation of the variable - // std::string code = "from IPython import get_ipython;"; - // code += var_repr_data + ',' + var_repr_metadata + "= get_ipython().display_formatter.format(" + var_name + ")"; - // py::gil_scoped_acquire acquire; - // exec(py::str(code)); - // } - // else - // { - // // The code has stopped on a breakpoint, we use the setExpression request - // // to get the rich representation of the variable - // std::string code = "get_ipython().display_formatter.format(" + var_name + ")"; - // int frame_id = message["arguments"]["frameId"].get(); - // int seq = message["seq"].get(); - // nl::json request = { - // {"type", "request"}, - // {"command", "evaluate"}, - // {"seq", seq+1}, - // {"arguments", { - // {"expression", code}, - // {"frameId", frame_id}, - // {"context", "clipboard"} - // }} - // }; - // nl::json request_reply = forward_message(request); - // std::string result = request_reply["body"]["result"]; - - // py::gil_scoped_acquire acquire; - // std::string exec_code = var_repr_data + ',' + var_repr_metadata + "= eval(str(" + result + "))"; - // exec(py::str(exec_code)); - // } - - // py::gil_scoped_acquire acquire; - // py::object variables = py::globals(); - // py::object repr_data = variables[py::str(var_repr_data)]; - // py::object repr_metadata = variables[py::str(var_repr_metadata)]; - // nl::json body = { - // {"data", {}}, - // {"metadata", {}} - // }; - // for (const py::handle& key : repr_data) - // { - // std::string data_key = py::str(key); - // body["data"][data_key] = repr_data[key]; - // if (repr_metadata.contains(key)) - // { - // body["metadata"][data_key] = repr_metadata[key]; - // } - // } - // PyDict_DelItem(variables.ptr(), py::str(var_repr_data).ptr()); - // PyDict_DelItem(variables.ptr(), py::str(var_repr_metadata).ptr()); - // reply["body"] = body; - // reply["success"] = true; - // return reply; + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]} + }; + + std::string var_name; + try { + var_name = message["arguments"]["variableName"].get(); + } catch (const nl::json::exception& e) { + reply["body"] = { + {"data", { + {"application/json", { + {"type", "error"}, + {"message", "Invalid variable name in request"}, + {"details", std::string(e.what())} + }} + }}, + {"metadata", { + {"application/json", { + {"error", true}, + {"errorType", "invalid_request"} + }} + }} + }; + return reply; + } + + int frame_id = 0; + if(message["arguments"].contains("frameId")) { + frame_id = message["arguments"]["frameId"].get(); + } + + nl::json var_data, var_metadata; + bool success = false; + + auto stopped_threads = base_type::get_stopped_threads(); + if(!stopped_threads.empty()) { + success = get_variable_info_from_lldb(var_name, frame_id, var_data, var_metadata, message["seq"].get() + 1); + } else { + // When there is no breakpoint hit, return a placeholder structure + var_data["application/json"] = { + {"type", "unavailable"}, + {"name", var_name}, + {"message", "Variable not accessible - no active debugging session"}, + {"suggestions", { + "Set a breakpoint and run the program", + "Ensure the variable is in scope", + "Check if the program is currently stopped" + }} + }; + var_metadata["application/json"] = { + {"available", false}, + {"reason", "no_stopped_threads"} + }; + success = true; + } + + if(success) { + reply["body"] = { + {"data", var_data}, + {"metadata", var_metadata} + }; + reply["success"] = true; + } else { + reply["body"] = { + {"data", { + {"application/json", { + {"type", "error"}, + {"name", var_name}, + {"message", "Variable '" + var_name + "' not found or not accessible"}, + {"frameId", frame_id}, + {"suggestions", { + "Check variable name spelling", + "Ensure variable is in current scope", + "Verify the frame ID is correct" + }} + }} + }}, + {"metadata", { + {"application/json", { + {"error", true}, + {"errorType", "variable_not_found"} + }} + }} + }; + } + + log_debug("[debugger::rich_inspect_variables_request] Reply:\n" + reply.dump(4)); + return reply; } nl::json debugger::stack_trace_request(const nl::json& message) @@ -431,15 +577,15 @@ namespace xcpp int requested_thread_id = message["arguments"]["threadId"]; auto stopped_threads = base_type::get_stopped_threads(); - + if (stopped_threads.empty()) { nl::json reply = forward_message(message); return reply; } - + int actual_thread_id; nl::json modified_message = message; - + if (requested_thread_id == 1 && !stopped_threads.empty()) { actual_thread_id = *stopped_threads.begin(); modified_message["arguments"]["threadId"] = actual_thread_id; @@ -451,11 +597,11 @@ namespace xcpp else { actual_thread_id = *stopped_threads.begin(); modified_message["arguments"]["threadId"] = actual_thread_id; - std::clog << "XDEBUGGER: Thread " << requested_thread_id - << " not found in stopped threads, using " << actual_thread_id << std::endl; + std::clog << "XDEBUGGER: Thread " << requested_thread_id + << " not found in stopped threads, using " << actual_thread_id << std::endl; } - for(auto x : stopped_threads) + for (auto x : stopped_threads) { log_debug("Stopped thread ID: " + std::to_string(x)); } @@ -463,22 +609,67 @@ namespace xcpp log_debug("[debugger::stack_trace_request]:\n" + modified_message.dump(4)); nl::json reply = forward_message(modified_message); - + if (!reply.contains("body") || !reply["body"].contains("stackFrames")) { return reply; } - + auto& stack_frames = reply["body"]["stackFrames"]; - for (auto it = stack_frames.begin(); it != stack_frames.end(); ++it) { - if (it->contains("source") && (*it)["source"].contains("path") && - (*it)["source"]["path"] == "") { - stack_frames.erase(it); - break; + nl::json filtered_frames = nl::json::array(); + + for (auto& frame : stack_frames) + { + if (frame.contains("source") && frame["source"].contains("path")) + { + std::string path = frame["source"]["path"]; + std::string name = frame["source"]["name"]; + // Check if path is in the format input_line_ + if (name.find("input_line_") != std::string::npos) + { + // Map sequential filename to hash if mapping exists + auto it = m_sequential_to_hash.find(name); + if (it != m_sequential_to_hash.end()) + { + frame["source"]["path"] = it->second; + // Set name to last part of path (filename) + std::string filename = it->second.substr(it->second.find_last_of("/\\") + 1); + frame["source"]["name"] = filename; + } else { + std::string code, hash_file_name; + // Extract execution count number from "input_line_" + int exec_count = -1; + std::string prefix = "input_line_"; + if (name.compare(0, prefix.size(), prefix) == 0) + { + try + { + exec_count = std::stoi(name.substr(prefix.size())); + } + catch (...) + { + exec_count = 0; + } + } + + if (exec_count != -1) { + code = m_interpreter->get_code_from_execution_count(exec_count); + hash_file_name = get_cell_temporary_file(code); + frame["source"]["path"] = hash_file_name; + frame["source"]["name"] = hash_file_name.substr(hash_file_name.find_last_of("/\\") + 1); + // Update mappings if not already present + m_hash_to_sequential[hash_file_name].push_back(name); + m_sequential_to_hash[name] = hash_file_name; + } + } + filtered_frames.push_back(frame); + } } } - + + reply["body"]["stackFrames"] = filtered_frames; + #ifdef WIN32 - for (auto& frame : stack_frames) { + for (auto& frame : reply["body"]["stackFrames"]) { if (frame.contains("source") && frame["source"].contains("path")) { std::string path = frame["source"]["path"]; std::replace(path.begin(), path.end(), '\\', '/'); @@ -486,7 +677,9 @@ namespace xcpp } } #endif - + + log_debug("Stack trace response: " + reply.dump(4)); + return reply; } @@ -555,6 +748,51 @@ namespace xcpp return reply; } + nl::json debugger::source_request(const nl::json& message) + { + std::string sourcePath; + try + { + sourcePath = message["arguments"]["source"]["path"].get(); + } + catch(nl::json::type_error& e) + { + std::clog << e.what() << std::endl; + } + catch(...) + { + std::clog << "XDEBUGGER: Unknown issue" << std::endl; + } + + std::ifstream ifs(sourcePath, std::ios::in); + if(!ifs.is_open()) + { + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", false}, + {"command", message["command"]}, + {"message", "source unavailable"}, + {"body", {{}}} + }; + return reply; + } + + std::string content((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + + nl::json reply = { + {"type", "response"}, + {"request_seq", message["seq"]}, + {"success", true}, + {"command", message["command"]}, + {"body", { + {"content", content} + }} + }; + log_debug("[debugger::source_request reply]:\n" + reply.dump(4)); + return reply; + } + std::string debugger::get_cell_temporary_file(const std::string& code) const { return get_cell_tmp_file(code); diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index f4c7ad11..f4a4e152 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -178,6 +178,7 @@ __get_cxx_version () std::string err; m_code_to_execution_count_map[code].push_back(execution_count); + m_execution_count_to_code_map[execution_count] = code; // Attempt normal evaluation try From 5ab3a13ca02ce6fb0006b9bf64d4e18c8cf4a1a3 Mon Sep 17 00:00:00 2001 From: kr-2003 Date: Wed, 2 Jul 2025 14:01:44 +0530 Subject: [PATCH 19/19] redirection handle --- src/xinterpreter.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/src/xinterpreter.cpp b/src/xinterpreter.cpp index f4a4e152..6b9f5755 100644 --- a/src/xinterpreter.cpp +++ b/src/xinterpreter.cpp @@ -19,6 +19,8 @@ #include "xinspect.hpp" #include "xmagics/os.hpp" #include +#include +#include #ifndef EMSCRIPTEN #include "xmagics/xassist.hpp" #endif @@ -27,6 +29,22 @@ using Args = std::vector; +static int g_stdin_pipe[2] = {-1, -1}; +static int g_stdout_pipe[2] = {-1, -1}; +static int g_stderr_pipe[2] = {-1, -1}; +static bool g_pipes_initialized = false; + +// Initialize global pipes once +void init_global_pipes() { + if (!g_pipes_initialized) { + if (pipe(g_stdin_pipe) == 0 && pipe(g_stdout_pipe) == 0 && pipe(g_stderr_pipe) == 0) { + g_pipes_initialized = true; + } else { + throw std::runtime_error("Failed to create global pipes"); + } + } +} + void* createInterpreter(const Args& ExtraArgs = {}) { Args ClangArgs = {/*"-xc++"*/"-v"}; @@ -56,13 +74,61 @@ void* createInterpreter(const Args& ExtraArgs = {}) return Cpp::CreateInterpreter(ClangArgs /*, {"-cuda"}*/); } - return Cpp::CreateInterpreter(ClangArgs, {}, true); + init_global_pipes(); + + return Cpp::CreateInterpreter(ClangArgs, {}, true, g_stdin_pipe[0], g_stdout_pipe[1], g_stderr_pipe[1]); } using namespace std::placeholders; namespace xcpp { + class GlobalPipeRedirectRAII { + private: + std::string captured_stderr; + + std::string read_all_from_fd(int fd) { + std::string result; + char buffer[4096]; + ssize_t bytes_read; + + // Set non-blocking to avoid hanging + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + while ((bytes_read = read(fd, buffer, sizeof(buffer))) > 0) { + result.append(buffer, bytes_read); + } + + // Restore original flags + fcntl(fd, F_SETFL, flags); + return result; + } + + void write_to_fd(int fd, const std::string& data) { + write(fd, data.c_str(), data.length()); + } + + public: + GlobalPipeRedirectRAII(const std::string& input_data = "") { + if (!input_data.empty() && g_stdin_pipe[1] != -1) { + write_to_fd(g_stdin_pipe[1], input_data); + close(g_stdin_pipe[1]); // Close write end to signal EOF + } + } + ~GlobalPipeRedirectRAII() { + // Read any pending output from pipes + std::string stdout_content = read_all_from_fd(g_stdout_pipe[0]); + captured_stderr = read_all_from_fd(g_stderr_pipe[0]); + + // Output captured stdout content + if (!stdout_content.empty()) { + std::cout << stdout_content; + } + } + + const std::string& get_captured_stderr() const { return captured_stderr; } + }; struct StreamRedirectRAII { std::string &err; StreamRedirectRAII(std::string &e) : err(e) { @@ -180,11 +246,20 @@ __get_cxx_version () m_code_to_execution_count_map[code].push_back(execution_count); m_execution_count_to_code_map[execution_count] = code; + bool use_out_of_process = g_pipes_initialized; + // Attempt normal evaluation try { - StreamRedirectRAII R(err); - compilation_result = Cpp::Process(code.c_str()); + if (use_out_of_process) { + std::string input_for_process = ""; + GlobalPipeRedirectRAII redirect(input_for_process); + compilation_result = Cpp::Process(code.c_str()); + err = redirect.get_captured_stderr(); + } else { + StreamRedirectRAII R(err); + compilation_result = Cpp::Process(code.c_str()); + } } catch (std::exception& e) {