summaryrefslogtreecommitdiff
path: root/src/core/debugger
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/debugger')
-rw-r--r--src/core/debugger/debugger.cpp19
-rw-r--r--src/core/debugger/debugger.h8
-rw-r--r--src/core/debugger/debugger_interface.h8
-rw-r--r--src/core/debugger/gdbstub.cpp139
-rw-r--r--src/core/debugger/gdbstub.h3
5 files changed, 151 insertions, 26 deletions
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index ab3940922..ac64d2f9d 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -44,12 +44,14 @@ static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
44 44
45enum class SignalType { 45enum class SignalType {
46 Stopped, 46 Stopped,
47 Watchpoint,
47 ShuttingDown, 48 ShuttingDown,
48}; 49};
49 50
50struct SignalInfo { 51struct SignalInfo {
51 SignalType type; 52 SignalType type;
52 Kernel::KThread* thread; 53 Kernel::KThread* thread;
54 const Kernel::DebugWatchpoint* watchpoint;
53}; 55};
54 56
55namespace Core { 57namespace Core {
@@ -157,13 +159,19 @@ private:
157 void PipeData(std::span<const u8> data) { 159 void PipeData(std::span<const u8> data) {
158 switch (info.type) { 160 switch (info.type) {
159 case SignalType::Stopped: 161 case SignalType::Stopped:
162 case SignalType::Watchpoint:
160 // Stop emulation. 163 // Stop emulation.
161 PauseEmulation(); 164 PauseEmulation();
162 165
163 // Notify the client. 166 // Notify the client.
164 active_thread = info.thread; 167 active_thread = info.thread;
165 UpdateActiveThread(); 168 UpdateActiveThread();
166 frontend->Stopped(active_thread); 169
170 if (info.type == SignalType::Watchpoint) {
171 frontend->Watchpoint(active_thread, *info.watchpoint);
172 } else {
173 frontend->Stopped(active_thread);
174 }
167 175
168 break; 176 break;
169 case SignalType::ShuttingDown: 177 case SignalType::ShuttingDown:
@@ -290,12 +298,17 @@ Debugger::Debugger(Core::System& system, u16 port) {
290Debugger::~Debugger() = default; 298Debugger::~Debugger() = default;
291 299
292bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) { 300bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
293 return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread}); 301 return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr});
302}
303
304bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread,
305 const Kernel::DebugWatchpoint& watch) {
306 return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch});
294} 307}
295 308
296void Debugger::NotifyShutdown() { 309void Debugger::NotifyShutdown() {
297 if (impl) { 310 if (impl) {
298 impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr}); 311 impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr});
299 } 312 }
300} 313}
301 314
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
index f9738ca3d..b2f503376 100644
--- a/src/core/debugger/debugger.h
+++ b/src/core/debugger/debugger.h
@@ -9,7 +9,8 @@
9 9
10namespace Kernel { 10namespace Kernel {
11class KThread; 11class KThread;
12} 12struct DebugWatchpoint;
13} // namespace Kernel
13 14
14namespace Core { 15namespace Core {
15class System; 16class System;
@@ -40,6 +41,11 @@ public:
40 */ 41 */
41 void NotifyShutdown(); 42 void NotifyShutdown();
42 43
44 /*
45 * Notify the debugger that the given thread has stopped due to hitting a watchpoint.
46 */
47 bool NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch);
48
43private: 49private:
44 std::unique_ptr<DebuggerImpl> impl; 50 std::unique_ptr<DebuggerImpl> impl;
45}; 51};
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
index c0bb4ecaf..5b31edc43 100644
--- a/src/core/debugger/debugger_interface.h
+++ b/src/core/debugger/debugger_interface.h
@@ -11,7 +11,8 @@
11 11
12namespace Kernel { 12namespace Kernel {
13class KThread; 13class KThread;
14} 14struct DebugWatchpoint;
15} // namespace Kernel
15 16
16namespace Core { 17namespace Core {
17 18
@@ -71,6 +72,11 @@ public:
71 */ 72 */
72 virtual void ShuttingDown() = 0; 73 virtual void ShuttingDown() = 0;
73 74
75 /*
76 * Called when emulation has stopped on a watchpoint.
77 */
78 virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
79
74 /** 80 /**
75 * Called when new data is asynchronously received on the client socket. 81 * Called when new data is asynchronously received on the client socket.
76 * A list of actions to perform is returned. 82 * A list of actions to perform is returned.
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 52e76f659..f5e9a303d 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -112,6 +112,23 @@ void GDBStub::Stopped(Kernel::KThread* thread) {
112 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)); 112 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
113} 113}
114 114
115void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
116 const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
117
118 switch (watch.type) {
119 case Kernel::DebugWatchpointType::Read:
120 SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address));
121 break;
122 case Kernel::DebugWatchpointType::Write:
123 SendReply(fmt::format("{}watch:{:x};", status, watch.start_address));
124 break;
125 case Kernel::DebugWatchpointType::ReadOrWrite:
126 default:
127 SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address));
128 break;
129 }
130}
131
115std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) { 132std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
116 std::vector<DebuggerAction> actions; 133 std::vector<DebuggerAction> actions;
117 current_command.insert(current_command.end(), data.begin(), data.end()); 134 current_command.insert(current_command.end(), data.begin(), data.end());
@@ -278,41 +295,121 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
278 case 'c': 295 case 'c':
279 actions.push_back(DebuggerAction::Continue); 296 actions.push_back(DebuggerAction::Continue);
280 break; 297 break;
281 case 'Z': { 298 case 'Z':
282 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; 299 HandleBreakpointInsert(command);
283 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 300 break;
301 case 'z':
302 HandleBreakpointRemove(command);
303 break;
304 default:
305 SendReply(GDB_STUB_REPLY_EMPTY);
306 break;
307 }
308}
284 309
285 if (system.Memory().IsValidVirtualAddress(addr)) { 310enum class BreakpointType {
286 replaced_instructions[addr] = system.Memory().Read32(addr); 311 Software = 0,
287 system.Memory().Write32(addr, arch->BreakpointInstruction()); 312 Hardware = 1,
288 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); 313 WriteWatch = 2,
314 ReadWatch = 3,
315 AccessWatch = 4,
316};
317
318void GDBStub::HandleBreakpointInsert(std::string_view command) {
319 const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
320 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
321 const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
322 command.begin() + 1};
323 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
324 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
325
326 if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
327 SendReply(GDB_STUB_REPLY_ERR);
328 return;
329 }
289 330
290 SendReply(GDB_STUB_REPLY_OK); 331 bool success{};
291 } else { 332
292 SendReply(GDB_STUB_REPLY_ERR); 333 switch (type) {
293 } 334 case BreakpointType::Software:
335 replaced_instructions[addr] = system.Memory().Read32(addr);
336 system.Memory().Write32(addr, arch->BreakpointInstruction());
337 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
338 success = true;
339 break;
340 case BreakpointType::WriteWatch:
341 success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
342 Kernel::DebugWatchpointType::Write);
294 break; 343 break;
344 case BreakpointType::ReadWatch:
345 success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
346 Kernel::DebugWatchpointType::Read);
347 break;
348 case BreakpointType::AccessWatch:
349 success = system.CurrentProcess()->InsertWatchpoint(
350 system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
351 break;
352 case BreakpointType::Hardware:
353 default:
354 SendReply(GDB_STUB_REPLY_EMPTY);
355 return;
356 }
357
358 if (success) {
359 SendReply(GDB_STUB_REPLY_OK);
360 } else {
361 SendReply(GDB_STUB_REPLY_ERR);
295 } 362 }
296 case 'z': { 363}
297 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; 364
298 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; 365void GDBStub::HandleBreakpointRemove(std::string_view command) {
366 const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
367 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
368 const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
369 command.begin() + 1};
370 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
371 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
372
373 if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
374 SendReply(GDB_STUB_REPLY_ERR);
375 return;
376 }
377
378 bool success{};
299 379
380 switch (type) {
381 case BreakpointType::Software: {
300 const auto orig_insn{replaced_instructions.find(addr)}; 382 const auto orig_insn{replaced_instructions.find(addr)};
301 if (system.Memory().IsValidVirtualAddress(addr) && 383 if (orig_insn != replaced_instructions.end()) {
302 orig_insn != replaced_instructions.end()) {
303 system.Memory().Write32(addr, orig_insn->second); 384 system.Memory().Write32(addr, orig_insn->second);
304 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32)); 385 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
305 replaced_instructions.erase(addr); 386 replaced_instructions.erase(addr);
306 387 success = true;
307 SendReply(GDB_STUB_REPLY_OK);
308 } else {
309 SendReply(GDB_STUB_REPLY_ERR);
310 } 388 }
311 break; 389 break;
312 } 390 }
391 case BreakpointType::WriteWatch:
392 success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
393 Kernel::DebugWatchpointType::Write);
394 break;
395 case BreakpointType::ReadWatch:
396 success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
397 Kernel::DebugWatchpointType::Read);
398 break;
399 case BreakpointType::AccessWatch:
400 success = system.CurrentProcess()->RemoveWatchpoint(
401 system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
402 break;
403 case BreakpointType::Hardware:
313 default: 404 default:
314 SendReply(GDB_STUB_REPLY_EMPTY); 405 SendReply(GDB_STUB_REPLY_EMPTY);
315 break; 406 return;
407 }
408
409 if (success) {
410 SendReply(GDB_STUB_REPLY_OK);
411 } else {
412 SendReply(GDB_STUB_REPLY_ERR);
316 } 413 }
317} 414}
318 415
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index ec934c77e..0b0f56e4b 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -24,6 +24,7 @@ public:
24 void Connected() override; 24 void Connected() override;
25 void Stopped(Kernel::KThread* thread) override; 25 void Stopped(Kernel::KThread* thread) override;
26 void ShuttingDown() override; 26 void ShuttingDown() override;
27 void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
27 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; 28 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
28 29
29private: 30private:
@@ -31,6 +32,8 @@ private:
31 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); 32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
32 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); 33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
33 void HandleQuery(std::string_view command); 34 void HandleQuery(std::string_view command);
35 void HandleBreakpointInsert(std::string_view command);
36 void HandleBreakpointRemove(std::string_view command);
34 std::vector<char>::const_iterator CommandEnd() const; 37 std::vector<char>::const_iterator CommandEnd() const;
35 std::optional<std::string> DetachCommand(); 38 std::optional<std::string> DetachCommand();
36 Kernel::KThread* GetThreadByID(u64 thread_id); 39 Kernel::KThread* GetThreadByID(u64 thread_id);