summaryrefslogtreecommitdiff
path: root/src/core/debugger/debugger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/debugger/debugger.cpp')
-rw-r--r--src/core/debugger/debugger.cpp161
1 files changed, 93 insertions, 68 deletions
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 339f971e6..1a8e02e6a 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
27 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); 27 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
28 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; 28 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
29 c(received_data); 29 c(received_data);
30 AsyncReceiveInto(r, buffer, c);
30 } 31 }
31
32 AsyncReceiveInto(r, buffer, c);
33 }); 32 });
34} 33}
35 34
35template <typename Callback>
36static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
37 acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
38 if (!error.failed()) {
39 c(peer_socket);
40 AsyncAccept(acceptor, c);
41 }
42 });
43}
44
36template <typename Readable, typename Buffer> 45template <typename Readable, typename Buffer>
37static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { 46static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
38 static_assert(std::is_trivial_v<Buffer>); 47 static_assert(std::is_trivial_v<Buffer>);
@@ -59,9 +68,7 @@ namespace Core {
59 68
60class DebuggerImpl : public DebuggerBackend { 69class DebuggerImpl : public DebuggerBackend {
61public: 70public:
62 explicit DebuggerImpl(Core::System& system_, u16 port) 71 explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
63 : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
64 frontend = std::make_unique<GDBStub>(*this, system);
65 InitializeServer(port); 72 InitializeServer(port);
66 } 73 }
67 74
@@ -70,39 +77,42 @@ public:
70 } 77 }
71 78
72 bool SignalDebugger(SignalInfo signal_info) { 79 bool SignalDebugger(SignalInfo signal_info) {
73 { 80 std::scoped_lock lk{connection_lock};
74 std::scoped_lock lk{connection_lock};
75 81
76 if (stopped) { 82 if (stopped || !state) {
77 // Do not notify the debugger about another event. 83 // Do not notify the debugger about another event.
78 // It should be ignored. 84 // It should be ignored.
79 return false; 85 return false;
80 }
81
82 // Set up the state.
83 stopped = true;
84 info = signal_info;
85 } 86 }
86 87
88 // Set up the state.
89 stopped = true;
90 state->info = signal_info;
91
87 // Write a single byte into the pipe to wake up the debug interface. 92 // Write a single byte into the pipe to wake up the debug interface.
88 boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); 93 boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
94
89 return true; 95 return true;
90 } 96 }
91 97
98 // These functions are callbacks from the frontend, and the lock will be held.
99 // There is no need to relock it.
100
92 std::span<const u8> ReadFromClient() override { 101 std::span<const u8> ReadFromClient() override {
93 return ReceiveInto(client_socket, client_data); 102 return ReceiveInto(state->client_socket, state->client_data);
94 } 103 }
95 104
96 void WriteToClient(std::span<const u8> data) override { 105 void WriteToClient(std::span<const u8> data) override {
97 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); 106 boost::asio::write(state->client_socket,
107 boost::asio::buffer(data.data(), data.size_bytes()));
98 } 108 }
99 109
100 void SetActiveThread(Kernel::KThread* thread) override { 110 void SetActiveThread(Kernel::KThread* thread) override {
101 active_thread = thread; 111 state->active_thread = thread;
102 } 112 }
103 113
104 Kernel::KThread* GetActiveThread() override { 114 Kernel::KThread* GetActiveThread() override {
105 return active_thread; 115 return state->active_thread;
106 } 116 }
107 117
108private: 118private:
@@ -113,65 +123,78 @@ private:
113 123
114 // Run the connection thread. 124 // Run the connection thread.
115 connection_thread = std::jthread([&, port](std::stop_token stop_token) { 125 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
126 Common::SetCurrentThreadName("Debugger");
127
116 try { 128 try {
117 // Initialize the listening socket and accept a new client. 129 // Initialize the listening socket and accept a new client.
118 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; 130 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
119 tcp::acceptor acceptor{io_context, endpoint}; 131 tcp::acceptor acceptor{io_context, endpoint};
120 132
121 acceptor.async_accept(client_socket, [](const auto&) {}); 133 AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
122 io_context.run_one();
123 io_context.restart();
124 134
125 if (stop_token.stop_requested()) { 135 while (!stop_token.stop_requested() && io_context.run()) {
126 return;
127 } 136 }
128
129 ThreadLoop(stop_token);
130 } catch (const std::exception& ex) { 137 } catch (const std::exception& ex) {
131 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); 138 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
132 } 139 }
133 }); 140 });
134 } 141 }
135 142
136 void ShutdownServer() { 143 void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
137 connection_thread.request_stop(); 144 LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
138 io_context.stop();
139 connection_thread.join();
140 }
141 145
142 void ThreadLoop(std::stop_token stop_token) { 146 std::scoped_lock lk{connection_lock};
143 Common::SetCurrentThreadName("Debugger"); 147
148 // Ensure everything is stopped.
149 PauseEmulation();
150
151 // Set up the new frontend.
152 frontend = std::make_unique<GDBStub>(*this, system);
153
154 // Set the new state. This will tear down any existing state.
155 state = ConnectionState{
156 .client_socket{std::move(peer)},
157 .signal_pipe{io_context},
158 .info{},
159 .active_thread{},
160 .client_data{},
161 .pipe_data{},
162 };
144 163
145 // Set up the client signals for new data. 164 // Set up the client signals for new data.
146 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); 165 AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
147 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); 166 AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
148 167
149 // Set the active thread. 168 // Set the active thread.
150 UpdateActiveThread(); 169 UpdateActiveThread();
151 170
152 // Set up the frontend. 171 // Set up the frontend.
153 frontend->Connected(); 172 frontend->Connected();
173 }
154 174
155 // Main event loop. 175 void ShutdownServer() {
156 while (!stop_token.stop_requested() && io_context.run()) { 176 connection_thread.request_stop();
157 } 177 io_context.stop();
178 connection_thread.join();
158 } 179 }
159 180
160 void PipeData(std::span<const u8> data) { 181 void PipeData(std::span<const u8> data) {
161 switch (info.type) { 182 std::scoped_lock lk{connection_lock};
183
184 switch (state->info.type) {
162 case SignalType::Stopped: 185 case SignalType::Stopped:
163 case SignalType::Watchpoint: 186 case SignalType::Watchpoint:
164 // Stop emulation. 187 // Stop emulation.
165 PauseEmulation(); 188 PauseEmulation();
166 189
167 // Notify the client. 190 // Notify the client.
168 active_thread = info.thread; 191 state->active_thread = state->info.thread;
169 UpdateActiveThread(); 192 UpdateActiveThread();
170 193
171 if (info.type == SignalType::Watchpoint) { 194 if (state->info.type == SignalType::Watchpoint) {
172 frontend->Watchpoint(active_thread, *info.watchpoint); 195 frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
173 } else { 196 } else {
174 frontend->Stopped(active_thread); 197 frontend->Stopped(state->active_thread);
175 } 198 }
176 199
177 break; 200 break;
@@ -179,8 +202,8 @@ private:
179 frontend->ShuttingDown(); 202 frontend->ShuttingDown();
180 203
181 // Wait for emulation to shut down gracefully now. 204 // Wait for emulation to shut down gracefully now.
182 signal_pipe.close(); 205 state->signal_pipe.close();
183 client_socket.shutdown(boost::asio::socket_base::shutdown_both); 206 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
184 LOG_INFO(Debug_GDBStub, "Shut down server"); 207 LOG_INFO(Debug_GDBStub, "Shut down server");
185 208
186 break; 209 break;
@@ -188,17 +211,16 @@ private:
188 } 211 }
189 212
190 void ClientData(std::span<const u8> data) { 213 void ClientData(std::span<const u8> data) {
214 std::scoped_lock lk{connection_lock};
215
191 const auto actions{frontend->ClientData(data)}; 216 const auto actions{frontend->ClientData(data)};
192 for (const auto action : actions) { 217 for (const auto action : actions) {
193 switch (action) { 218 switch (action) {
194 case DebuggerAction::Interrupt: { 219 case DebuggerAction::Interrupt: {
195 { 220 stopped = true;
196 std::scoped_lock lk{connection_lock};
197 stopped = true;
198 }
199 PauseEmulation(); 221 PauseEmulation();
200 UpdateActiveThread(); 222 UpdateActiveThread();
201 frontend->Stopped(active_thread); 223 frontend->Stopped(state->active_thread);
202 break; 224 break;
203 } 225 }
204 case DebuggerAction::Continue: 226 case DebuggerAction::Continue:
@@ -206,15 +228,15 @@ private:
206 break; 228 break;
207 case DebuggerAction::StepThreadUnlocked: 229 case DebuggerAction::StepThreadUnlocked:
208 MarkResumed([&] { 230 MarkResumed([&] {
209 active_thread->SetStepState(Kernel::StepState::StepPending); 231 state->active_thread->SetStepState(Kernel::StepState::StepPending);
210 active_thread->Resume(Kernel::SuspendType::Debug); 232 state->active_thread->Resume(Kernel::SuspendType::Debug);
211 ResumeEmulation(active_thread); 233 ResumeEmulation(state->active_thread);
212 }); 234 });
213 break; 235 break;
214 case DebuggerAction::StepThreadLocked: { 236 case DebuggerAction::StepThreadLocked: {
215 MarkResumed([&] { 237 MarkResumed([&] {
216 active_thread->SetStepState(Kernel::StepState::StepPending); 238 state->active_thread->SetStepState(Kernel::StepState::StepPending);
217 active_thread->Resume(Kernel::SuspendType::Debug); 239 state->active_thread->Resume(Kernel::SuspendType::Debug);
218 }); 240 });
219 break; 241 break;
220 } 242 }
@@ -254,15 +276,14 @@ private:
254 template <typename Callback> 276 template <typename Callback>
255 void MarkResumed(Callback&& cb) { 277 void MarkResumed(Callback&& cb) {
256 Kernel::KScopedSchedulerLock sl{system.Kernel()}; 278 Kernel::KScopedSchedulerLock sl{system.Kernel()};
257 std::scoped_lock cl{connection_lock};
258 stopped = false; 279 stopped = false;
259 cb(); 280 cb();
260 } 281 }
261 282
262 void UpdateActiveThread() { 283 void UpdateActiveThread() {
263 const auto& threads{ThreadList()}; 284 const auto& threads{ThreadList()};
264 if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { 285 if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
265 active_thread = threads[0]; 286 state->active_thread = threads[0];
266 } 287 }
267 } 288 }
268 289
@@ -274,18 +295,22 @@ private:
274 System& system; 295 System& system;
275 std::unique_ptr<DebuggerFrontend> frontend; 296 std::unique_ptr<DebuggerFrontend> frontend;
276 297
298 boost::asio::io_context io_context;
277 std::jthread connection_thread; 299 std::jthread connection_thread;
278 std::mutex connection_lock; 300 std::mutex connection_lock;
279 boost::asio::io_context io_context;
280 boost::process::async_pipe signal_pipe;
281 boost::asio::ip::tcp::socket client_socket;
282 301
283 SignalInfo info; 302 struct ConnectionState {
284 Kernel::KThread* active_thread; 303 boost::asio::ip::tcp::socket client_socket;
285 bool pipe_data; 304 boost::process::async_pipe signal_pipe;
286 bool stopped; 305
306 SignalInfo info;
307 Kernel::KThread* active_thread;
308 std::array<u8, 4096> client_data;
309 bool pipe_data;
310 };
287 311
288 std::array<u8, 4096> client_data; 312 std::optional<ConnectionState> state{};
313 bool stopped{};
289}; 314};
290 315
291Debugger::Debugger(Core::System& system, u16 port) { 316Debugger::Debugger(Core::System& system, u16 port) {