diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/debugger/debugger.cpp | 161 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub.cpp | 151 | ||||
| -rw-r--r-- | src/core/debugger/gdbstub.h | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table.h | 3 |
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 | ||
| 35 | template <typename Callback> | ||
| 36 | static 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 | |||
| 36 | template <typename Readable, typename Buffer> | 45 | template <typename Readable, typename Buffer> |
| 37 | static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { | 46 | static 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 | ||
| 60 | class DebuggerImpl : public DebuggerBackend { | 69 | class DebuggerImpl : public DebuggerBackend { |
| 61 | public: | 70 | public: |
| 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 | ||
| 108 | private: | 118 | private: |
| @@ -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 | ||
| 291 | Debugger::Debugger(Core::System& system, u16 port) { | 316 | Debugger::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 | ||
| 650 | constexpr 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 | |||
| 675 | static 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 | |||
| 684 | static 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 | |||
| 701 | static 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 | |||
| 727 | void 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 | |||
| 648 | Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { | 799 | Kernel::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 | } |