summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/arm/arm_interface.cpp45
-rw-r--r--src/core/arm/arm_interface.h14
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp45
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h9
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp45
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h9
-rw-r--r--src/core/debugger/debugger.cpp61
-rw-r--r--src/core/debugger/debugger.h5
-rw-r--r--src/core/debugger/debugger_interface.h9
-rw-r--r--src/core/debugger/gdbstub.cpp99
-rw-r--r--src/core/debugger/gdbstub.h2
-rw-r--r--src/core/hle/kernel/k_thread.h15
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/yuzu/CMakeLists.txt4
15 files changed, 247 insertions, 122 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 948cc318a..2bd720f08 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -768,6 +768,9 @@ create_target_directory_groups(core)
768 768
769target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 769target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
770target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) 770target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
771if (MINGW)
772 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
773endif()
771 774
772if (ENABLE_WEB_SERVICE) 775if (ENABLE_WEB_SERVICE)
773 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 776 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 1310f72bf..9b5a5ca57 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -11,6 +11,7 @@
11#include "core/core.h" 11#include "core/core.h"
12#include "core/debugger/debugger.h" 12#include "core/debugger/debugger.h"
13#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
14#include "core/hle/kernel/svc.h"
14#include "core/loader/loader.h" 15#include "core/loader/loader.h"
15#include "core/memory.h" 16#include "core/memory.h"
16 17
@@ -89,8 +90,48 @@ void ARM_Interface::LogBacktrace() const {
89 } 90 }
90} 91}
91 92
92bool ARM_Interface::ShouldStep() const { 93void ARM_Interface::Run() {
93 return system.DebuggerEnabled() && system.GetDebugger().IsStepping(); 94 using Kernel::StepState;
95 using Kernel::SuspendType;
96
97 while (true) {
98 Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
99 Dynarmic::HaltReason hr{};
100
101 // Notify the debugger and go to sleep if a step was performed
102 // and this thread has been scheduled again.
103 if (current_thread->GetStepState() == StepState::StepPerformed) {
104 system.GetDebugger().NotifyThreadStopped(current_thread);
105 current_thread->RequestSuspend(SuspendType::Debug);
106 break;
107 }
108
109 // Otherwise, run the thread.
110 if (current_thread->GetStepState() == StepState::StepPending) {
111 hr = StepJit();
112
113 if (Has(hr, step_thread)) {
114 current_thread->SetStepState(StepState::StepPerformed);
115 }
116 } else {
117 hr = RunJit();
118 }
119
120 // Notify the debugger and go to sleep if a breakpoint was hit.
121 if (Has(hr, breakpoint)) {
122 system.GetDebugger().NotifyThreadStopped(current_thread);
123 current_thread->RequestSuspend(Kernel::SuspendType::Debug);
124 break;
125 }
126
127 // Handle syscalls and scheduling (this may change the current thread)
128 if (Has(hr, svc_call)) {
129 Kernel::Svc::Call(system, GetSvcNumber());
130 }
131 if (Has(hr, break_loop) || !uses_wall_clock) {
132 break;
133 }
134 }
94} 135}
95 136
96} // namespace Core 137} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 7842c626b..66f6107e9 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,6 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
10#include <dynarmic/interface/halt_reason.h>
11
9#include "common/common_funcs.h" 12#include "common/common_funcs.h"
10#include "common/common_types.h" 13#include "common/common_types.h"
11#include "core/hardware_properties.h" 14#include "core/hardware_properties.h"
@@ -64,7 +67,7 @@ public:
64 static_assert(sizeof(ThreadContext64) == 0x320); 67 static_assert(sizeof(ThreadContext64) == 0x320);
65 68
66 /// Runs the CPU until an event happens 69 /// Runs the CPU until an event happens
67 virtual void Run() = 0; 70 void Run();
68 71
69 /// Clear all instruction cache 72 /// Clear all instruction cache
70 virtual void ClearInstructionCache() = 0; 73 virtual void ClearInstructionCache() = 0;
@@ -191,7 +194,10 @@ public:
191 194
192 void LogBacktrace() const; 195 void LogBacktrace() const;
193 196
194 bool ShouldStep() const; 197 static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
198 static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
199 static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
200 static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
195 201
196protected: 202protected:
197 /// System context that this ARM interface is running under. 203 /// System context that this ARM interface is running under.
@@ -200,6 +206,10 @@ protected:
200 bool uses_wall_clock; 206 bool uses_wall_clock;
201 207
202 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); 208 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
209
210 virtual Dynarmic::HaltReason RunJit() = 0;
211 virtual Dynarmic::HaltReason StepJit() = 0;
212 virtual u32 GetSvcNumber() const = 0;
203}; 213};
204 214
205} // namespace Core 215} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 894c1c527..7c82d0b96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -26,10 +26,6 @@ namespace Core {
26 26
27using namespace Common::Literals; 27using namespace Common::Literals;
28 28
29constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
30constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
31constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
32
33class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 29class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
34public: 30public:
35 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 31 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -82,8 +78,8 @@ public:
82 78
83 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 79 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
84 if (parent.system.DebuggerEnabled()) { 80 if (parent.system.DebuggerEnabled()) {
85 parent.breakpoint_pc = pc; 81 parent.jit.load()->Regs()[15] = pc;
86 parent.jit.load()->HaltExecution(breakpoint); 82 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
87 return; 83 return;
88 } 84 }
89 85
@@ -95,7 +91,7 @@ public:
95 91
96 void CallSVC(u32 swi) override { 92 void CallSVC(u32 swi) override {
97 parent.svc_swi = swi; 93 parent.svc_swi = swi;
98 parent.jit.load()->HaltExecution(svc_call); 94 parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
99 } 95 }
100 96
101 void AddTicks(u64 ticks) override { 97 void AddTicks(u64 ticks) override {
@@ -240,35 +236,16 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
240 return std::make_unique<Dynarmic::A32::Jit>(config); 236 return std::make_unique<Dynarmic::A32::Jit>(config);
241} 237}
242 238
243void ARM_Dynarmic_32::Run() { 239Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
244 while (true) { 240 return jit.load()->Run();
245 const auto hr = ShouldStep() ? jit.load()->Step() : jit.load()->Run(); 241}
246 if (Has(hr, svc_call)) {
247 Kernel::Svc::Call(system, svc_swi);
248 }
249
250 // Check to see if breakpoint is triggered.
251 // Recheck step condition in case stop is no longer desired.
252 Kernel::KThread* current_thread = system.Kernel().GetCurrentEmuThread();
253 if (Has(hr, breakpoint)) {
254 jit.load()->Regs()[15] = breakpoint_pc;
255 242
256 if (system.GetDebugger().NotifyThreadStopped(current_thread)) { 243Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
257 current_thread->RequestSuspend(Kernel::SuspendType::Debug); 244 return jit.load()->Step();
258 } 245}
259 break;
260 }
261 if (ShouldStep()) {
262 // When stepping, this should be the only thread running.
263 ASSERT(system.GetDebugger().NotifyThreadStopped(current_thread));
264 current_thread->RequestSuspend(Kernel::SuspendType::Debug);
265 break;
266 }
267 246
268 if (Has(hr, break_loop) || !uses_wall_clock) { 247u32 ARM_Dynarmic_32::GetSvcNumber() const {
269 break; 248 return svc_swi;
270 }
271 }
272} 249}
273 250
274ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 251ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 0557d5940..5b1d60005 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -41,7 +41,6 @@ public:
41 void SetVectorReg(int index, u128 value) override; 41 void SetVectorReg(int index, u128 value) override;
42 u32 GetPSTATE() const override; 42 u32 GetPSTATE() const override;
43 void SetPSTATE(u32 pstate) override; 43 void SetPSTATE(u32 pstate) override;
44 void Run() override;
45 VAddr GetTlsAddress() const override; 44 VAddr GetTlsAddress() const override;
46 void SetTlsAddress(VAddr address) override; 45 void SetTlsAddress(VAddr address) override;
47 void SetTPIDR_EL0(u64 value) override; 46 void SetTPIDR_EL0(u64 value) override;
@@ -69,6 +68,11 @@ public:
69 68
70 std::vector<BacktraceEntry> GetBacktrace() const override; 69 std::vector<BacktraceEntry> GetBacktrace() const override;
71 70
71protected:
72 Dynarmic::HaltReason RunJit() override;
73 Dynarmic::HaltReason StepJit() override;
74 u32 GetSvcNumber() const override;
75
72private: 76private:
73 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; 77 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
74 78
@@ -94,9 +98,6 @@ private:
94 98
95 // SVC callback 99 // SVC callback
96 u32 svc_swi{}; 100 u32 svc_swi{};
97
98 // Debug restart address
99 u32 breakpoint_pc{};
100}; 101};
101 102
102} // namespace Core 103} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1f596cfef..d4c67eafd 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -26,10 +26,6 @@ namespace Core {
26using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
27using namespace Common::Literals; 27using namespace Common::Literals;
28 28
29constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
30constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
31constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
32
33class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
34public: 30public:
35 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 31 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -123,8 +119,8 @@ public:
123 return; 119 return;
124 default: 120 default:
125 if (parent.system.DebuggerEnabled()) { 121 if (parent.system.DebuggerEnabled()) {
126 parent.breakpoint_pc = pc; 122 parent.jit.load()->SetPC(pc);
127 parent.jit.load()->HaltExecution(breakpoint); 123 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
128 return; 124 return;
129 } 125 }
130 126
@@ -136,7 +132,7 @@ public:
136 132
137 void CallSVC(u32 swi) override { 133 void CallSVC(u32 swi) override {
138 parent.svc_swi = swi; 134 parent.svc_swi = swi;
139 parent.jit.load()->HaltExecution(svc_call); 135 parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
140 } 136 }
141 137
142 void AddTicks(u64 ticks) override { 138 void AddTicks(u64 ticks) override {
@@ -300,35 +296,16 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
300 return std::make_shared<Dynarmic::A64::Jit>(config); 296 return std::make_shared<Dynarmic::A64::Jit>(config);
301} 297}
302 298
303void ARM_Dynarmic_64::Run() { 299Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
304 while (true) { 300 return jit.load()->Run();
305 const auto hr = jit.load()->Run(); 301}
306 if (Has(hr, svc_call)) {
307 Kernel::Svc::Call(system, svc_swi);
308 }
309
310 // Check to see if breakpoint is triggered.
311 // Recheck step condition in case stop is no longer desired.
312 Kernel::KThread* current_thread = system.Kernel().GetCurrentEmuThread();
313 if (Has(hr, breakpoint)) {
314 jit.load()->SetPC(breakpoint_pc);
315 302
316 if (system.GetDebugger().NotifyThreadStopped(current_thread)) { 303Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
317 current_thread->RequestSuspend(Kernel::SuspendType::Debug); 304 return jit.load()->Step();
318 } 305}
319 break;
320 }
321 if (ShouldStep()) {
322 // When stepping, this should be the only thread running.
323 ASSERT(system.GetDebugger().NotifyThreadStopped(current_thread));
324 current_thread->RequestSuspend(Kernel::SuspendType::Debug);
325 break;
326 }
327 306
328 if (Has(hr, break_loop) || !uses_wall_clock) { 307u32 ARM_Dynarmic_64::GetSvcNumber() const {
329 break; 308 return svc_swi;
330 }
331 }
332} 309}
333 310
334ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 311ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index aa7054e0c..abfbc3c3f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -39,7 +39,6 @@ public:
39 void SetVectorReg(int index, u128 value) override; 39 void SetVectorReg(int index, u128 value) override;
40 u32 GetPSTATE() const override; 40 u32 GetPSTATE() const override;
41 void SetPSTATE(u32 pstate) override; 41 void SetPSTATE(u32 pstate) override;
42 void Run() override;
43 VAddr GetTlsAddress() const override; 42 VAddr GetTlsAddress() const override;
44 void SetTlsAddress(VAddr address) override; 43 void SetTlsAddress(VAddr address) override;
45 void SetTPIDR_EL0(u64 value) override; 44 void SetTPIDR_EL0(u64 value) override;
@@ -63,6 +62,11 @@ public:
63 62
64 std::vector<BacktraceEntry> GetBacktrace() const override; 63 std::vector<BacktraceEntry> GetBacktrace() const override;
65 64
65protected:
66 Dynarmic::HaltReason RunJit() override;
67 Dynarmic::HaltReason StepJit() override;
68 u32 GetSvcNumber() const override;
69
66private: 70private:
67 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 71 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
68 std::size_t address_space_bits) const; 72 std::size_t address_space_bits) const;
@@ -87,9 +91,6 @@ private:
87 91
88 // SVC callback 92 // SVC callback
89 u32 svc_swi{}; 93 u32 svc_swi{};
90
91 // Debug restart address
92 u64 breakpoint_pc{};
93}; 94};
94 95
95} // namespace Core 96} // namespace Core
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 7a2012d3c..a73f2279d 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
4#include <mutex> 5#include <mutex>
5#include <thread> 6#include <thread>
6 7
@@ -84,31 +85,31 @@ public:
84 return active_thread; 85 return active_thread;
85 } 86 }
86 87
87 bool IsStepping() const {
88 return stepping;
89 }
90
91private: 88private:
92 void InitializeServer(u16 port) { 89 void InitializeServer(u16 port) {
93 using boost::asio::ip::tcp; 90 using boost::asio::ip::tcp;
94 91
95 LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port); 92 LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
96 93
97 // Initialize the listening socket and accept a new client.
98 tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
99 tcp::acceptor acceptor{io_context, endpoint};
100 client_socket = acceptor.accept();
101
102 // Run the connection thread. 94 // Run the connection thread.
103 connection_thread = std::jthread([&](std::stop_token stop_token) { 95 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
104 try { 96 try {
97 // Initialize the listening socket and accept a new client.
98 tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
99 tcp::acceptor acceptor{io_context, endpoint};
100
101 acceptor.async_accept(client_socket, [](const auto&) {});
102 io_context.run_one();
103 io_context.restart();
104
105 if (stop_token.stop_requested()) {
106 return;
107 }
108
105 ThreadLoop(stop_token); 109 ThreadLoop(stop_token);
106 } catch (const std::exception& ex) { 110 } catch (const std::exception& ex) {
107 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); 111 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
108 } 112 }
109
110 client_socket.shutdown(client_socket.shutdown_both);
111 client_socket.close();
112 }); 113 });
113 } 114 }
114 115
@@ -129,8 +130,7 @@ private:
129 AllCoreStop(); 130 AllCoreStop();
130 131
131 // Set the active thread. 132 // Set the active thread.
132 active_thread = ThreadList()[0]; 133 UpdateActiveThread();
133 active_thread->Resume(Kernel::SuspendType::Debug);
134 134
135 // Set up the frontend. 135 // Set up the frontend.
136 frontend->Connected(); 136 frontend->Connected();
@@ -142,7 +142,7 @@ private:
142 142
143 void PipeData(std::span<const u8> data) { 143 void PipeData(std::span<const u8> data) {
144 AllCoreStop(); 144 AllCoreStop();
145 active_thread->Resume(Kernel::SuspendType::Debug); 145 UpdateActiveThread();
146 frontend->Stopped(active_thread); 146 frontend->Stopped(active_thread);
147 } 147 }
148 148
@@ -156,18 +156,22 @@ private:
156 stopped = true; 156 stopped = true;
157 } 157 }
158 AllCoreStop(); 158 AllCoreStop();
159 active_thread = ThreadList()[0]; 159 UpdateActiveThread();
160 active_thread->Resume(Kernel::SuspendType::Debug);
161 frontend->Stopped(active_thread); 160 frontend->Stopped(active_thread);
162 break; 161 break;
163 } 162 }
164 case DebuggerAction::Continue: 163 case DebuggerAction::Continue:
165 stepping = false; 164 active_thread->SetStepState(Kernel::StepState::NotStepping);
166 ResumeInactiveThreads(); 165 ResumeInactiveThreads();
167 AllCoreResume(); 166 AllCoreResume();
168 break; 167 break;
169 case DebuggerAction::StepThread: 168 case DebuggerAction::StepThreadUnlocked:
170 stepping = true; 169 active_thread->SetStepState(Kernel::StepState::StepPending);
170 ResumeInactiveThreads();
171 AllCoreResume();
172 break;
173 case DebuggerAction::StepThreadLocked:
174 active_thread->SetStepState(Kernel::StepState::StepPending);
171 SuspendInactiveThreads(); 175 SuspendInactiveThreads();
172 AllCoreResume(); 176 AllCoreResume();
173 break; 177 break;
@@ -212,10 +216,20 @@ private:
212 for (auto* thread : ThreadList()) { 216 for (auto* thread : ThreadList()) {
213 if (thread != active_thread) { 217 if (thread != active_thread) {
214 thread->Resume(Kernel::SuspendType::Debug); 218 thread->Resume(Kernel::SuspendType::Debug);
219 thread->SetStepState(Kernel::StepState::NotStepping);
215 } 220 }
216 } 221 }
217 } 222 }
218 223
224 void UpdateActiveThread() {
225 const auto& threads{ThreadList()};
226 if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
227 active_thread = threads[0];
228 }
229 active_thread->Resume(Kernel::SuspendType::Debug);
230 active_thread->SetStepState(Kernel::StepState::NotStepping);
231 }
232
219 const std::vector<Kernel::KThread*>& ThreadList() { 233 const std::vector<Kernel::KThread*>& ThreadList() {
220 return system.GlobalSchedulerContext().GetThreadList(); 234 return system.GlobalSchedulerContext().GetThreadList();
221 } 235 }
@@ -233,7 +247,6 @@ private:
233 247
234 Kernel::KThread* active_thread; 248 Kernel::KThread* active_thread;
235 bool stopped; 249 bool stopped;
236 bool stepping;
237 250
238 std::array<u8, 4096> client_data; 251 std::array<u8, 4096> client_data;
239}; 252};
@@ -252,8 +265,4 @@ bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
252 return impl && impl->NotifyThreadStopped(thread); 265 return impl && impl->NotifyThreadStopped(thread);
253} 266}
254 267
255bool Debugger::IsStepping() const {
256 return impl && impl->IsStepping();
257}
258
259} // namespace Core 268} // namespace Core
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
index 7acd11815..ea36c6ab2 100644
--- a/src/core/debugger/debugger.h
+++ b/src/core/debugger/debugger.h
@@ -35,11 +35,6 @@ public:
35 */ 35 */
36 bool NotifyThreadStopped(Kernel::KThread* thread); 36 bool NotifyThreadStopped(Kernel::KThread* thread);
37 37
38 /**
39 * Returns whether a step is in progress.
40 */
41 bool IsStepping() const;
42
43private: 38private:
44 std::unique_ptr<DebuggerImpl> impl; 39 std::unique_ptr<DebuggerImpl> impl;
45}; 40};
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
index 0b357fcb5..6ae3dc091 100644
--- a/src/core/debugger/debugger_interface.h
+++ b/src/core/debugger/debugger_interface.h
@@ -16,10 +16,11 @@ class KThread;
16namespace Core { 16namespace Core {
17 17
18enum class DebuggerAction { 18enum class DebuggerAction {
19 Interrupt, // Stop emulation as soon as possible. 19 Interrupt, ///< Stop emulation as soon as possible.
20 Continue, // Resume emulation. 20 Continue, ///< Resume emulation.
21 StepThread, // Step the currently-active thread. 21 StepThreadLocked, ///< Step the currently-active thread without resuming others.
22 ShutdownEmulation, // Shut down the emulator. 22 StepThreadUnlocked, ///< Step the currently-active thread and resume others.
23 ShutdownEmulation, ///< Shut down the emulator.
23}; 24};
24 25
25class DebuggerBackend { 26class DebuggerBackend {
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 718c45952..44ebbe3e0 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -6,8 +6,7 @@
6#include <optional> 6#include <optional>
7#include <thread> 7#include <thread>
8 8
9#include <boost/asio.hpp> 9#include <boost/algorithm/string.hpp>
10#include <boost/process/async_pipe.hpp>
11 10
12#include "common/hex_util.h" 11#include "common/hex_util.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
@@ -114,6 +113,11 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
114 return; 113 return;
115 } 114 }
116 115
116 if (packet.starts_with("vCont")) {
117 HandleVCont(packet.substr(5), actions);
118 return;
119 }
120
117 std::string_view command{packet.substr(1, packet.size())}; 121 std::string_view command{packet.substr(1, packet.size())};
118 122
119 switch (packet[0]) { 123 switch (packet[0]) {
@@ -122,6 +126,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
122 s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; 126 s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
123 if (thread_id >= 1) { 127 if (thread_id >= 1) {
124 thread = GetThreadByID(thread_id); 128 thread = GetThreadByID(thread_id);
129 } else {
130 thread = backend.GetActiveThread();
125 } 131 }
126 132
127 if (thread) { 133 if (thread) {
@@ -141,6 +147,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
141 } 147 }
142 break; 148 break;
143 } 149 }
150 case 'Q':
144 case 'q': 151 case 'q':
145 HandleQuery(command); 152 HandleQuery(command);
146 break; 153 break;
@@ -204,7 +211,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
204 break; 211 break;
205 } 212 }
206 case 's': 213 case 's':
207 actions.push_back(DebuggerAction::StepThread); 214 actions.push_back(DebuggerAction::StepThreadLocked);
208 break; 215 break;
209 case 'C': 216 case 'C':
210 case 'c': 217 case 'c':
@@ -248,12 +255,47 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
248 } 255 }
249} 256}
250 257
258static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
259 switch (thread->GetWaitReasonForDebugging()) {
260 case Kernel::ThreadWaitReasonForDebugging::Sleep:
261 return "Sleep";
262 case Kernel::ThreadWaitReasonForDebugging::IPC:
263 return "IPC";
264 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
265 return "Synchronization";
266 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
267 return "ConditionVar";
268 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
269 return "Arbitration";
270 case Kernel::ThreadWaitReasonForDebugging::Suspended:
271 return "Suspended";
272 default:
273 return "Unknown";
274 }
275}
276
277static std::string GetThreadState(const Kernel::KThread* thread) {
278 switch (thread->GetState()) {
279 case Kernel::ThreadState::Initialized:
280 return "Initialized";
281 case Kernel::ThreadState::Waiting:
282 return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
283 case Kernel::ThreadState::Runnable:
284 return "Runnable";
285 case Kernel::ThreadState::Terminated:
286 return "Terminated";
287 default:
288 return "Unknown";
289 }
290}
291
251void GDBStub::HandleQuery(std::string_view command) { 292void GDBStub::HandleQuery(std::string_view command) {
252 if (command.starts_with("TStatus")) { 293 if (command.starts_with("TStatus")) {
253 // no tracepoint support 294 // no tracepoint support
254 SendReply("T0"); 295 SendReply("T0");
255 } else if (command.starts_with("Supported")) { 296 } else if (command.starts_with("Supported")) {
256 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+"); 297 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
298 "vContSupported+;QStartNoAckMode+");
257 } else if (command.starts_with("Xfer:features:read:target.xml:")) { 299 } else if (command.starts_with("Xfer:features:read:target.xml:")) {
258 const auto offset{command.substr(30)}; 300 const auto offset{command.substr(30)};
259 const auto amount{command.substr(command.find(',') + 1)}; 301 const auto amount{command.substr(command.find(',') + 1)};
@@ -297,18 +339,57 @@ void GDBStub::HandleQuery(std::string_view command) {
297 339
298 const auto& threads = system.GlobalSchedulerContext().GetThreadList(); 340 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
299 for (const auto& thread : threads) { 341 for (const auto& thread : threads) {
300 buffer += 342 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)",
301 fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}"/>)", 343 thread->GetThreadID(), thread->GetActiveCore(),
302 thread->GetThreadID(), thread->GetActiveCore(), thread->GetThreadID()); 344 thread->GetThreadID(), GetThreadState(thread));
303 } 345 }
304 346
305 buffer += "</threads>"; 347 buffer += "</threads>";
306 SendReply(buffer); 348 SendReply(buffer);
349 } else if (command.starts_with("Attached")) {
350 SendReply("0");
351 } else if (command.starts_with("StartNoAckMode")) {
352 no_ack = true;
353 SendReply(GDB_STUB_REPLY_OK);
307 } else { 354 } else {
308 SendReply(GDB_STUB_REPLY_EMPTY); 355 SendReply(GDB_STUB_REPLY_EMPTY);
309 } 356 }
310} 357}
311 358
359void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
360 if (command == "?") {
361 // Continuing and stepping are supported
362 // (signal is ignored, but required for GDB to use vCont)
363 SendReply("vCont;c;C;s;S");
364 return;
365 }
366
367 Kernel::KThread* stepped_thread{nullptr};
368 bool lock_execution{true};
369
370 std::vector<std::string> entries;
371 boost::split(entries, command.substr(1), boost::is_any_of(";"));
372 for (const auto& thread_action : entries) {
373 std::vector<std::string> parts;
374 boost::split(parts, thread_action, boost::is_any_of(":"));
375
376 if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
377 lock_execution = false;
378 }
379 if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
380 stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
381 }
382 }
383
384 if (stepped_thread) {
385 backend.SetActiveThread(stepped_thread);
386 actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
387 : DebuggerAction::StepThreadUnlocked);
388 } else {
389 actions.push_back(DebuggerAction::Continue);
390 }
391}
392
312Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 393Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
313 const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; 394 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
314 for (auto* thread : threads) { 395 for (auto* thread : threads) {
@@ -374,6 +455,10 @@ void GDBStub::SendReply(std::string_view data) {
374} 455}
375 456
376void GDBStub::SendStatus(char status) { 457void GDBStub::SendStatus(char status) {
458 if (no_ack) {
459 return;
460 }
461
377 std::array<u8, 1> buf = {static_cast<u8>(status)}; 462 std::array<u8, 1> buf = {static_cast<u8>(status)};
378 LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); 463 LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
379 backend.WriteToClient(buf); 464 backend.WriteToClient(buf);
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index b93a3a511..6f8ac2263 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -28,6 +28,7 @@ public:
28private: 28private:
29 void ProcessData(std::vector<DebuggerAction>& actions); 29 void ProcessData(std::vector<DebuggerAction>& actions);
30 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); 30 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
31 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
31 void HandleQuery(std::string_view command); 32 void HandleQuery(std::string_view command);
32 std::vector<char>::const_iterator CommandEnd() const; 33 std::vector<char>::const_iterator CommandEnd() const;
33 std::optional<std::string> DetachCommand(); 34 std::optional<std::string> DetachCommand();
@@ -42,6 +43,7 @@ private:
42 std::unique_ptr<GDBStubArch> arch; 43 std::unique_ptr<GDBStubArch> arch;
43 std::vector<char> current_command; 44 std::vector<char> current_command;
44 std::map<VAddr, u32> replaced_instructions; 45 std::map<VAddr, u32> replaced_instructions;
46 bool no_ack{};
45}; 47};
46 48
47} // namespace Core 49} // namespace Core
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index b55a922ab..60ae0da78 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -100,6 +100,12 @@ enum class ThreadWaitReasonForDebugging : u32 {
100 Suspended, ///< Thread is waiting due to process suspension 100 Suspended, ///< Thread is waiting due to process suspension
101}; 101};
102 102
103enum class StepState : u32 {
104 NotStepping, ///< Thread is not currently stepping
105 StepPending, ///< Thread will step when next scheduled
106 StepPerformed, ///< Thread has stepped, waiting to be scheduled again
107};
108
103[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); 109[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
104[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); 110[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
105[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); 111[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -267,6 +273,14 @@ public:
267 273
268 void SetState(ThreadState state); 274 void SetState(ThreadState state);
269 275
276 [[nodiscard]] StepState GetStepState() const {
277 return step_state;
278 }
279
280 void SetStepState(StepState state) {
281 step_state = state;
282 }
283
270 [[nodiscard]] s64 GetLastScheduledTick() const { 284 [[nodiscard]] s64 GetLastScheduledTick() const {
271 return last_scheduled_tick; 285 return last_scheduled_tick;
272 } 286 }
@@ -769,6 +783,7 @@ private:
769 std::shared_ptr<Common::Fiber> host_context{}; 783 std::shared_ptr<Common::Fiber> host_context{};
770 bool is_single_core{}; 784 bool is_single_core{};
771 ThreadType thread_type{}; 785 ThreadType thread_type{};
786 StepState step_state{};
772 std::mutex dummy_wait_lock; 787 std::mutex dummy_wait_lock;
773 std::condition_variable dummy_wait_cv; 788 std::condition_variable dummy_wait_cv;
774 789
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 6a6325e38..256695804 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -277,3 +277,7 @@ else()
277 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 277 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
278 ) 278 )
279endif() 279endif()
280
281if (ARCHITECTURE_x86_64)
282 target_link_libraries(video_core PRIVATE dynarmic)
283endif()
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 404acdd05..07df9675d 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -319,3 +319,7 @@ endif()
319if (NOT APPLE) 319if (NOT APPLE)
320 target_compile_definitions(yuzu PRIVATE HAS_OPENGL) 320 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
321endif() 321endif()
322
323if (ARCHITECTURE_x86_64)
324 target_link_libraries(yuzu PRIVATE dynarmic)
325endif()