diff --git a/src/gdb-stub/CMakeLists.txt b/src/gdb-stub/CMakeLists.txt index ca23c4c1..2b3e2d80 100644 --- a/src/gdb-stub/CMakeLists.txt +++ b/src/gdb-stub/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(gdb-stub ${SRC_FILES}) momo_assign_source_group(${SRC_FILES}) target_link_libraries(gdb-stub PRIVATE - common + emulator-common ) target_include_directories(gdb-stub INTERFACE diff --git a/src/gdb-stub/async_handler.cpp b/src/gdb-stub/async_handler.cpp new file mode 100644 index 00000000..8afb0ce3 --- /dev/null +++ b/src/gdb-stub/async_handler.cpp @@ -0,0 +1,77 @@ +#include "async_handler.hpp" +#include + +using namespace std::chrono_literals; + +namespace gdb_stub +{ + async_handler::async_handler(handler_function h) + : handler_(std::move(h)) + { + this->stop_ = false; + } + + async_handler::~async_handler() + { + this->stop_ = true; + this->run_ = false; + + if (this->runner_.joinable()) + { + this->runner_.join(); + } + } + + void async_handler::pause() + { + this->run_ = false; + + while (this->is_running_ && !this->stop_) + { + std::this_thread::sleep_for(1ms); + } + } + + void async_handler::run() + { + if (this->stop_) + { + return; + } + + this->run_ = true; + + while (!this->is_running_ && !this->stop_) + { + std::this_thread::sleep_for(1ms); + } + } + + bool async_handler::is_running() const + { + return this->is_running_; + } + + void async_handler::work() + { + while (true) + { + while (!this->run_ && !this->stop_) + { + std::this_thread::sleep_for(10ms); + } + + if (this->stop_) + { + break; + } + + const auto _ = utils::finally([this] { + this->is_running_ = false; // + }); + + this->is_running_ = true; + this->handler_(this->run_); + } + } +} diff --git a/src/gdb-stub/async_handler.hpp b/src/gdb-stub/async_handler.hpp new file mode 100644 index 00000000..30bfd769 --- /dev/null +++ b/src/gdb-stub/async_handler.hpp @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include +#include + +namespace gdb_stub +{ + class async_handler + { + public: + using handler = void(const std::atomic_bool& should_run); + using handler_function = std::function; + + async_handler(handler_function handler); + ~async_handler(); + + async_handler(async_handler&&) = delete; + async_handler(const async_handler&) = delete; + + async_handler& operator=(async_handler&&) = delete; + async_handler& operator=(const async_handler&) = delete; + + void run(); + void pause(); + bool is_running() const; + + private: + std::atomic_bool run_{false}; + std::atomic_bool stop_{false}; + std::atomic_bool is_running_{false}; + + handler_function handler_{}; + std::thread runner_{}; + + void work(); + }; +} diff --git a/src/gdb-stub/checksum.hpp b/src/gdb-stub/checksum.hpp new file mode 100644 index 00000000..1ad71bc1 --- /dev/null +++ b/src/gdb-stub/checksum.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace gdb_stub +{ + constexpr size_t CHECKSUM_SIZE = 2; + + inline uint8_t compute_checksum(const std::string_view data) + { + uint8_t checksum = 0; + for (const auto c : data) + { + checksum += static_cast(c); + } + + return checksum; + } + + inline std::string compute_checksum_as_string(const std::string_view data) + { + const auto checksum = compute_checksum(data); + + std::stringstream stream{}; + stream << std::hex << checksum; + return stream.str(); + } +} diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp index 1ed91092..a587fb79 100644 --- a/src/gdb-stub/gdb_stub.cpp +++ b/src/gdb-stub/gdb_stub.cpp @@ -1,15 +1,14 @@ #include "gdb_stub.hpp" -#include -#include #include +#include "stream_processor.hpp" +#include "checksum.hpp" + namespace gdb_stub { namespace { - constexpr size_t CHECKSUM_SIZE = 2; - enum class continuation_event { none, @@ -18,29 +17,9 @@ namespace gdb_stub step, }; - uint8_t compute_checksum(const std::string_view data) - { - uint8_t csum = 0; - for (const auto c : data) - { - csum += static_cast(c); - } - - return csum; - } - - std::string compute_checksum_string(const std::string_view data) - { - const auto checksum = compute_checksum(data); - - std::stringstream stream{}; - stream << std::hex << checksum; - return stream.str(); - } - bool send_packet_reply(const network::tcp_client_socket& socket, const std::string_view data) { - const auto checksum = compute_checksum_string(data); + const auto checksum = compute_checksum_as_string(data); return socket.send("$" + std::string(data) + "#" + checksum); } @@ -55,85 +34,14 @@ namespace gdb_stub return server.accept(); } - struct packet_queue - { - std::string buffer{}; - std::queue packets{}; - - void enqueue(const std::string& data) - { - buffer.append(data); - this->process(); - } - - void process() - { - while (true) - { - this->trim_start(); - - const auto end = this->buffer.find_first_of('#'); - if (end == std::string::npos) - { - break; - } - - const auto packet_size = end + CHECKSUM_SIZE + 1; - - if (packet_size > this->buffer.size()) - { - break; - } - - auto packet = this->buffer.substr(0, packet_size); - this->buffer.erase(0, packet_size); - - this->enqueue_packet(std::move(packet)); - } - } - - void enqueue_packet(std::string packet) - { - constexpr auto END_BYTES = CHECKSUM_SIZE + 1; - - if (packet.size() < (END_BYTES + 1) // - || packet.front() != '$' // - || packet[packet.size() - END_BYTES] != '#') - { - return; - } - - const auto checksum = strtoul(packet.c_str() + packet.size() - CHECKSUM_SIZE, nullptr, 16); - assert((checksum & 0xFF) == checksum); - - packet.erase(packet.begin()); - packet.erase(packet.size() - END_BYTES, END_BYTES); - - const auto computed_checksum = compute_checksum(packet); - - if (computed_checksum == checksum) - { - this->packets.push(std::move(packet)); - } - } - - void trim_start() - { - while (!this->buffer.empty() && this->buffer.front() != '$') - { - buffer.erase(buffer.begin()); - } - } - }; - - void read_from_socket(packet_queue& queue, network::tcp_client_socket& client) + void read_from_socket(stream_processor& processor, network::tcp_client_socket& client) { while (client.is_ready(true)) { - auto packet = client.receive(); - if (packet) + const auto data = client.receive(); + if (data) { - queue.enqueue(std::move(*packet)); + processor.push_stream_data(*data); } } } @@ -208,7 +116,7 @@ namespace gdb_stub bool run_gdb_stub(const network::address& bind_address) { - packet_queue queue{}; + stream_processor processor{}; auto client = accept_client(bind_address); if (!client) @@ -220,12 +128,12 @@ namespace gdb_stub while (client.is_valid()) { - read_from_socket(queue, client); + read_from_socket(processor, client); - while (!queue.packets.empty()) + while (processor.has_packet()) { - process_packet(client, queue.packets.front()); - queue.packets.pop(); + const auto packet = processor.get_next_packet(); + process_packet(client, packet); } } diff --git a/src/gdb-stub/stream_processor.cpp b/src/gdb-stub/stream_processor.cpp new file mode 100644 index 00000000..ba3aabac --- /dev/null +++ b/src/gdb-stub/stream_processor.cpp @@ -0,0 +1,93 @@ +#include "stream_processor.hpp" +#include "checksum.hpp" + +#include + +namespace gdb_stub +{ + namespace + { + void trim_data_stream_start(std::string& stream) + { + while (!stream.empty() && stream.front() != '$') + { + stream.erase(stream.begin()); + } + } + } + + bool stream_processor::has_packet() const + { + return !this->packets_.empty(); + } + + std::string stream_processor::get_next_packet() + { + if (this->packets_.empty()) + { + throw std::runtime_error("No packet available"); + } + + auto packet = std::move(this->packets_.front()); + this->packets_.pop(); + + return packet; + } + + void stream_processor::push_stream_data(const std::string& data) + { + this->stream_.append(data); + this->process_data_stream(); + } + + void stream_processor::process_data_stream() + { + while (true) + { + trim_data_stream_start(this->stream_); + + const auto end = this->stream_.find_first_of('#'); + if (end == std::string::npos) + { + break; + } + + const auto packet_size = end + CHECKSUM_SIZE + 1; + + if (packet_size > this->stream_.size()) + { + break; + } + + auto packet = this->stream_.substr(0, packet_size); + this->stream_.erase(0, packet_size); + + this->enqueue_packet(std::move(packet)); + } + } + + void stream_processor::enqueue_packet(std::string packet) + { + constexpr auto END_BYTES = CHECKSUM_SIZE + 1; + + if (packet.size() < (END_BYTES + 1) // + || packet.front() != '$' // + || packet[packet.size() - END_BYTES] != '#') + { + return; + } + + const auto checksum = strtoul(packet.c_str() + packet.size() - CHECKSUM_SIZE, nullptr, 16); + assert((checksum & 0xFF) == checksum); + + packet.erase(packet.begin()); + packet.erase(packet.size() - END_BYTES, END_BYTES); + + const auto computed_checksum = compute_checksum(packet); + + if (computed_checksum == checksum) + { + this->packets_.push(std::move(packet)); + } + } +} diff --git a/src/gdb-stub/stream_processor.hpp b/src/gdb-stub/stream_processor.hpp new file mode 100644 index 00000000..c6b48442 --- /dev/null +++ b/src/gdb-stub/stream_processor.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +namespace gdb_stub +{ + class stream_processor + { + public: + bool has_packet() const; + std::string get_next_packet(); + void push_stream_data(const std::string& data); + + private: + std::string stream_{}; + std::queue packets_{}; + + void process_data_stream(); + void enqueue_packet(std::string packet); + }; +}