summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar liamwhite2022-11-12 21:04:00 -0500
committerGravatar GitHub2022-11-12 21:04:00 -0500
commit040a01a5ddf1dc3c8a3c992034803f54e9f2a7af (patch)
treeda068a23b8637fc094a31f7336c088c988a99fd7
parentMerge pull request #9235 from goldenx86/ignorearm (diff)
parentgdbstub: add ams monitor commands (diff)
downloadyuzu-040a01a5ddf1dc3c8a3c992034803f54e9f2a7af.tar.gz
yuzu-040a01a5ddf1dc3c8a3c992034803f54e9f2a7af.tar.xz
yuzu-040a01a5ddf1dc3c8a3c992034803f54e9f2a7af.zip
Merge pull request #9225 from liamwhite/debugger-instance
Debugger improvements
Diffstat (limited to '')
-rw-r--r--src/core/debugger/debugger.cpp161
-rw-r--r--src/core/debugger/gdbstub.cpp151
-rw-r--r--src/core/debugger/gdbstub.h1
-rw-r--r--src/core/hle/kernel/k_page_table.h3
4 files changed, 248 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) {
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 884229c77..a64a9ac64 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
606 } else if (command.starts_with("StartNoAckMode")) { 606 } else if (command.starts_with("StartNoAckMode")) {
607 no_ack = true; 607 no_ack = true;
608 SendReply(GDB_STUB_REPLY_OK); 608 SendReply(GDB_STUB_REPLY_OK);
609 } else if (command.starts_with("Rcmd,")) {
610 HandleRcmd(Common::HexStringToVector(command.substr(5), false));
609 } else { 611 } else {
610 SendReply(GDB_STUB_REPLY_EMPTY); 612 SendReply(GDB_STUB_REPLY_EMPTY);
611 } 613 }
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
645 } 647 }
646} 648}
647 649
650constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
651 {"----- Free -----", Kernel::Svc::MemoryState::Free},
652 {"Io ", Kernel::Svc::MemoryState::Io},
653 {"Static ", Kernel::Svc::MemoryState::Static},
654 {"Code ", Kernel::Svc::MemoryState::Code},
655 {"CodeData ", Kernel::Svc::MemoryState::CodeData},
656 {"Normal ", Kernel::Svc::MemoryState::Normal},
657 {"Shared ", Kernel::Svc::MemoryState::Shared},
658 {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
659 {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
660 {"Ipc ", Kernel::Svc::MemoryState::Ipc},
661 {"Stack ", Kernel::Svc::MemoryState::Stack},
662 {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
663 {"Transfered ", Kernel::Svc::MemoryState::Transfered},
664 {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
665 {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
666 {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
667 {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
668 {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
669 {"Kernel ", Kernel::Svc::MemoryState::Kernel},
670 {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
671 {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
672 {"Coverage ", Kernel::Svc::MemoryState::Coverage},
673}};
674
675static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
676 for (size_t i = 0; i < MemoryStateNames.size(); i++) {
677 if (std::get<1>(MemoryStateNames[i]) == state) {
678 return std::get<0>(MemoryStateNames[i]);
679 }
680 }
681 return "Unknown ";
682}
683
684static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
685 if (info.state == Kernel::Svc::MemoryState::Free) {
686 return " ";
687 }
688
689 switch (info.permission) {
690 case Kernel::Svc::MemoryPermission::ReadExecute:
691 return "r-x";
692 case Kernel::Svc::MemoryPermission::Read:
693 return "r--";
694 case Kernel::Svc::MemoryPermission::ReadWrite:
695 return "rw-";
696 default:
697 return "---";
698 }
699}
700
701static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
702 Kernel::Svc::MemoryInfo mem_info;
703 VAddr cur_addr{base};
704
705 // Expect: r-x Code (.text)
706 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
707 cur_addr = mem_info.base_address + mem_info.size;
708 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
709 mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
710 return cur_addr - 1;
711 }
712
713 // Expect: r-- Code (.rodata)
714 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
715 cur_addr = mem_info.base_address + mem_info.size;
716 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
717 mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
718 return cur_addr - 1;
719 }
720
721 // Expect: rw- CodeData (.data)
722 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
723 cur_addr = mem_info.base_address + mem_info.size;
724 return cur_addr - 1;
725}
726
727void GDBStub::HandleRcmd(const std::vector<u8>& command) {
728 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
729 std::string reply;
730
731 auto* process = system.CurrentProcess();
732 auto& page_table = process->PageTable();
733
734 if (command_str == "get info") {
735 Loader::AppLoader::Modules modules;
736 system.GetAppLoader().ReadNSOModules(modules);
737
738 reply = fmt::format("Process: {:#x} ({})\n"
739 "Program Id: {:#018x}\n",
740 process->GetProcessID(), process->GetName(), process->GetProgramID());
741 reply +=
742 fmt::format("Layout:\n"
743 " Alias: {:#012x} - {:#012x}\n"
744 " Heap: {:#012x} - {:#012x}\n"
745 " Aslr: {:#012x} - {:#012x}\n"
746 " Stack: {:#012x} - {:#012x}\n"
747 "Modules:\n",
748 page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
749 page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
750 page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
751 page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
752
753 for (const auto& [vaddr, name] : modules) {
754 reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
755 GetModuleEnd(page_table, vaddr), name);
756 }
757 } else if (command_str == "get mappings") {
758 reply = "Mappings:\n";
759 VAddr cur_addr = 0;
760
761 while (true) {
762 using MemoryAttribute = Kernel::Svc::MemoryAttribute;
763
764 auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
765
766 if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
767 mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
768 const char* state = GetMemoryStateName(mem_info.state);
769 const char* perm = GetMemoryPermissionString(mem_info);
770
771 const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
772 const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
773 const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
774 const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
775
776 reply +=
777 fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
778 mem_info.base_address, mem_info.base_address + mem_info.size - 1,
779 perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
780 }
781
782 const uintptr_t next_address = mem_info.base_address + mem_info.size;
783 if (next_address <= cur_addr) {
784 break;
785 }
786
787 cur_addr = next_address;
788 }
789 } else if (command_str == "help") {
790 reply = "Commands:\n get info\n get mappings\n";
791 } else {
792 reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
793 }
794
795 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
796 SendReply(Common::HexToString(reply_span, false));
797}
798
648Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 799Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
649 const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; 800 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
650 for (auto* thread : threads) { 801 for (auto* thread : threads) {
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 0b0f56e4b..368197920 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -32,6 +32,7 @@ private:
32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); 32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); 33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
34 void HandleQuery(std::string_view command); 34 void HandleQuery(std::string_view command);
35 void HandleRcmd(const std::vector<u8>& command);
35 void HandleBreakpointInsert(std::string_view command); 36 void HandleBreakpointInsert(std::string_view command);
36 void HandleBreakpointRemove(std::string_view command); 37 void HandleBreakpointRemove(std::string_view command);
37 std::vector<char>::const_iterator CommandEnd() const; 38 std::vector<char>::const_iterator CommandEnd() const;
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 950850291..f1ca785d7 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -320,6 +320,9 @@ public:
320 constexpr VAddr GetAliasCodeRegionStart() const { 320 constexpr VAddr GetAliasCodeRegionStart() const {
321 return m_alias_code_region_start; 321 return m_alias_code_region_start;
322 } 322 }
323 constexpr VAddr GetAliasCodeRegionEnd() const {
324 return m_alias_code_region_end;
325 }
323 constexpr VAddr GetAliasCodeRegionSize() const { 326 constexpr VAddr GetAliasCodeRegionSize() const {
324 return m_alias_code_region_end - m_alias_code_region_start; 327 return m_alias_code_region_end - m_alias_code_region_start;
325 } 328 }