diff options
| author | liamwhite <liamwhite@users.noreply.github.com> | 2022-11-12 21:04:00 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-11-12 21:04:00 -0500 | 
| commit | 040a01a5ddf1dc3c8a3c992034803f54e9f2a7af (patch) | |
| tree | da068a23b8637fc094a31f7336c088c988a99fd7 | |
| parent | 8cc5ad8742a2cc4bc0c8c3eb6a9034604ca2f81b (diff) | |
| parent | 18123ff958b0a4d877dab45a54637245c3b296ba (diff) | |
Merge pull request #9225 from liamwhite/debugger-instance
Debugger improvements
| -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) {                  const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);                  std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};                  c(received_data); +                AsyncReceiveInto(r, buffer, c);              } - -            AsyncReceiveInto(r, buffer, c);          });  } +template <typename Callback> +static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) { +    acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) { +        if (!error.failed()) { +            c(peer_socket); +            AsyncAccept(acceptor, c); +        } +    }); +} +  template <typename Readable, typename Buffer>  static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {      static_assert(std::is_trivial_v<Buffer>); @@ -59,9 +68,7 @@ namespace Core {  class DebuggerImpl : public DebuggerBackend {  public: -    explicit DebuggerImpl(Core::System& system_, u16 port) -        : system{system_}, signal_pipe{io_context}, client_socket{io_context} { -        frontend = std::make_unique<GDBStub>(*this, system); +    explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {          InitializeServer(port);      } @@ -70,39 +77,42 @@ public:      }      bool SignalDebugger(SignalInfo signal_info) { -        { -            std::scoped_lock lk{connection_lock}; +        std::scoped_lock lk{connection_lock}; -            if (stopped) { -                // Do not notify the debugger about another event. -                // It should be ignored. -                return false; -            } - -            // Set up the state. -            stopped = true; -            info = signal_info; +        if (stopped || !state) { +            // Do not notify the debugger about another event. +            // It should be ignored. +            return false;          } +        // Set up the state. +        stopped = true; +        state->info = signal_info; +          // Write a single byte into the pipe to wake up the debug interface. -        boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); +        boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); +          return true;      } +    // These functions are callbacks from the frontend, and the lock will be held. +    // There is no need to relock it. +      std::span<const u8> ReadFromClient() override { -        return ReceiveInto(client_socket, client_data); +        return ReceiveInto(state->client_socket, state->client_data);      }      void WriteToClient(std::span<const u8> data) override { -        boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); +        boost::asio::write(state->client_socket, +                           boost::asio::buffer(data.data(), data.size_bytes()));      }      void SetActiveThread(Kernel::KThread* thread) override { -        active_thread = thread; +        state->active_thread = thread;      }      Kernel::KThread* GetActiveThread() override { -        return active_thread; +        return state->active_thread;      }  private: @@ -113,65 +123,78 @@ private:          // Run the connection thread.          connection_thread = std::jthread([&, port](std::stop_token stop_token) { +            Common::SetCurrentThreadName("Debugger"); +              try {                  // Initialize the listening socket and accept a new client.                  tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};                  tcp::acceptor acceptor{io_context, endpoint}; -                acceptor.async_accept(client_socket, [](const auto&) {}); -                io_context.run_one(); -                io_context.restart(); +                AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); }); -                if (stop_token.stop_requested()) { -                    return; +                while (!stop_token.stop_requested() && io_context.run()) {                  } - -                ThreadLoop(stop_token);              } catch (const std::exception& ex) {                  LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());              }          });      } -    void ShutdownServer() { -        connection_thread.request_stop(); -        io_context.stop(); -        connection_thread.join(); -    } +    void AcceptConnection(boost::asio::ip::tcp::socket&& peer) { +        LOG_INFO(Debug_GDBStub, "Accepting new peer connection"); -    void ThreadLoop(std::stop_token stop_token) { -        Common::SetCurrentThreadName("Debugger"); +        std::scoped_lock lk{connection_lock}; + +        // Ensure everything is stopped. +        PauseEmulation(); + +        // Set up the new frontend. +        frontend = std::make_unique<GDBStub>(*this, system); + +        // Set the new state. This will tear down any existing state. +        state = ConnectionState{ +            .client_socket{std::move(peer)}, +            .signal_pipe{io_context}, +            .info{}, +            .active_thread{}, +            .client_data{}, +            .pipe_data{}, +        };          // Set up the client signals for new data. -        AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); -        AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); +        AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); }); +        AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });          // Set the active thread.          UpdateActiveThread();          // Set up the frontend.          frontend->Connected(); +    } -        // Main event loop. -        while (!stop_token.stop_requested() && io_context.run()) { -        } +    void ShutdownServer() { +        connection_thread.request_stop(); +        io_context.stop(); +        connection_thread.join();      }      void PipeData(std::span<const u8> data) { -        switch (info.type) { +        std::scoped_lock lk{connection_lock}; + +        switch (state->info.type) {          case SignalType::Stopped:          case SignalType::Watchpoint:              // Stop emulation.              PauseEmulation();              // Notify the client. -            active_thread = info.thread; +            state->active_thread = state->info.thread;              UpdateActiveThread(); -            if (info.type == SignalType::Watchpoint) { -                frontend->Watchpoint(active_thread, *info.watchpoint); +            if (state->info.type == SignalType::Watchpoint) { +                frontend->Watchpoint(state->active_thread, *state->info.watchpoint);              } else { -                frontend->Stopped(active_thread); +                frontend->Stopped(state->active_thread);              }              break; @@ -179,8 +202,8 @@ private:              frontend->ShuttingDown();              // Wait for emulation to shut down gracefully now. -            signal_pipe.close(); -            client_socket.shutdown(boost::asio::socket_base::shutdown_both); +            state->signal_pipe.close(); +            state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);              LOG_INFO(Debug_GDBStub, "Shut down server");              break; @@ -188,17 +211,16 @@ private:      }      void ClientData(std::span<const u8> data) { +        std::scoped_lock lk{connection_lock}; +          const auto actions{frontend->ClientData(data)};          for (const auto action : actions) {              switch (action) {              case DebuggerAction::Interrupt: { -                { -                    std::scoped_lock lk{connection_lock}; -                    stopped = true; -                } +                stopped = true;                  PauseEmulation();                  UpdateActiveThread(); -                frontend->Stopped(active_thread); +                frontend->Stopped(state->active_thread);                  break;              }              case DebuggerAction::Continue: @@ -206,15 +228,15 @@ private:                  break;              case DebuggerAction::StepThreadUnlocked:                  MarkResumed([&] { -                    active_thread->SetStepState(Kernel::StepState::StepPending); -                    active_thread->Resume(Kernel::SuspendType::Debug); -                    ResumeEmulation(active_thread); +                    state->active_thread->SetStepState(Kernel::StepState::StepPending); +                    state->active_thread->Resume(Kernel::SuspendType::Debug); +                    ResumeEmulation(state->active_thread);                  });                  break;              case DebuggerAction::StepThreadLocked: {                  MarkResumed([&] { -                    active_thread->SetStepState(Kernel::StepState::StepPending); -                    active_thread->Resume(Kernel::SuspendType::Debug); +                    state->active_thread->SetStepState(Kernel::StepState::StepPending); +                    state->active_thread->Resume(Kernel::SuspendType::Debug);                  });                  break;              } @@ -254,15 +276,14 @@ private:      template <typename Callback>      void MarkResumed(Callback&& cb) {          Kernel::KScopedSchedulerLock sl{system.Kernel()}; -        std::scoped_lock cl{connection_lock};          stopped = false;          cb();      }      void UpdateActiveThread() {          const auto& threads{ThreadList()}; -        if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { -            active_thread = threads[0]; +        if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { +            state->active_thread = threads[0];          }      } @@ -274,18 +295,22 @@ private:      System& system;      std::unique_ptr<DebuggerFrontend> frontend; +    boost::asio::io_context io_context;      std::jthread connection_thread;      std::mutex connection_lock; -    boost::asio::io_context io_context; -    boost::process::async_pipe signal_pipe; -    boost::asio::ip::tcp::socket client_socket; -    SignalInfo info; -    Kernel::KThread* active_thread; -    bool pipe_data; -    bool stopped; +    struct ConnectionState { +        boost::asio::ip::tcp::socket client_socket; +        boost::process::async_pipe signal_pipe; + +        SignalInfo info; +        Kernel::KThread* active_thread; +        std::array<u8, 4096> client_data; +        bool pipe_data; +    }; -    std::array<u8, 4096> client_data; +    std::optional<ConnectionState> state{}; +    bool stopped{};  };  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) {      } else if (command.starts_with("StartNoAckMode")) {          no_ack = true;          SendReply(GDB_STUB_REPLY_OK); +    } else if (command.starts_with("Rcmd,")) { +        HandleRcmd(Common::HexStringToVector(command.substr(5), false));      } else {          SendReply(GDB_STUB_REPLY_EMPTY);      } @@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&      }  } +constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ +    {"----- Free -----", Kernel::Svc::MemoryState::Free}, +    {"Io              ", Kernel::Svc::MemoryState::Io}, +    {"Static          ", Kernel::Svc::MemoryState::Static}, +    {"Code            ", Kernel::Svc::MemoryState::Code}, +    {"CodeData        ", Kernel::Svc::MemoryState::CodeData}, +    {"Normal          ", Kernel::Svc::MemoryState::Normal}, +    {"Shared          ", Kernel::Svc::MemoryState::Shared}, +    {"AliasCode       ", Kernel::Svc::MemoryState::AliasCode}, +    {"AliasCodeData   ", Kernel::Svc::MemoryState::AliasCodeData}, +    {"Ipc             ", Kernel::Svc::MemoryState::Ipc}, +    {"Stack           ", Kernel::Svc::MemoryState::Stack}, +    {"ThreadLocal     ", Kernel::Svc::MemoryState::ThreadLocal}, +    {"Transfered      ", Kernel::Svc::MemoryState::Transfered}, +    {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, +    {"SharedCode      ", Kernel::Svc::MemoryState::SharedCode}, +    {"Inaccessible    ", Kernel::Svc::MemoryState::Inaccessible}, +    {"NonSecureIpc    ", Kernel::Svc::MemoryState::NonSecureIpc}, +    {"NonDeviceIpc    ", Kernel::Svc::MemoryState::NonDeviceIpc}, +    {"Kernel          ", Kernel::Svc::MemoryState::Kernel}, +    {"GeneratedCode   ", Kernel::Svc::MemoryState::GeneratedCode}, +    {"CodeOut         ", Kernel::Svc::MemoryState::CodeOut}, +    {"Coverage        ", Kernel::Svc::MemoryState::Coverage}, +}}; + +static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { +    for (size_t i = 0; i < MemoryStateNames.size(); i++) { +        if (std::get<1>(MemoryStateNames[i]) == state) { +            return std::get<0>(MemoryStateNames[i]); +        } +    } +    return "Unknown         "; +} + +static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { +    if (info.state == Kernel::Svc::MemoryState::Free) { +        return "   "; +    } + +    switch (info.permission) { +    case Kernel::Svc::MemoryPermission::ReadExecute: +        return "r-x"; +    case Kernel::Svc::MemoryPermission::Read: +        return "r--"; +    case Kernel::Svc::MemoryPermission::ReadWrite: +        return "rw-"; +    default: +        return "---"; +    } +} + +static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { +    Kernel::Svc::MemoryInfo mem_info; +    VAddr cur_addr{base}; + +    // Expect: r-x Code (.text) +    mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); +    cur_addr = mem_info.base_address + mem_info.size; +    if (mem_info.state != Kernel::Svc::MemoryState::Code || +        mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { +        return cur_addr - 1; +    } + +    // Expect: r-- Code (.rodata) +    mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); +    cur_addr = mem_info.base_address + mem_info.size; +    if (mem_info.state != Kernel::Svc::MemoryState::Code || +        mem_info.permission != Kernel::Svc::MemoryPermission::Read) { +        return cur_addr - 1; +    } + +    // Expect: rw- CodeData (.data) +    mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); +    cur_addr = mem_info.base_address + mem_info.size; +    return cur_addr - 1; +} + +void GDBStub::HandleRcmd(const std::vector<u8>& command) { +    std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; +    std::string reply; + +    auto* process = system.CurrentProcess(); +    auto& page_table = process->PageTable(); + +    if (command_str == "get info") { +        Loader::AppLoader::Modules modules; +        system.GetAppLoader().ReadNSOModules(modules); + +        reply = fmt::format("Process:     {:#x} ({})\n" +                            "Program Id:  {:#018x}\n", +                            process->GetProcessID(), process->GetName(), process->GetProgramID()); +        reply += +            fmt::format("Layout:\n" +                        "  Alias: {:#012x} - {:#012x}\n" +                        "  Heap:  {:#012x} - {:#012x}\n" +                        "  Aslr:  {:#012x} - {:#012x}\n" +                        "  Stack: {:#012x} - {:#012x}\n" +                        "Modules:\n", +                        page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(), +                        page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(), +                        page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(), +                        page_table.GetStackRegionStart(), page_table.GetStackRegionEnd()); + +        for (const auto& [vaddr, name] : modules) { +            reply += fmt::format("  {:#012x} - {:#012x} {}\n", vaddr, +                                 GetModuleEnd(page_table, vaddr), name); +        } +    } else if (command_str == "get mappings") { +        reply = "Mappings:\n"; +        VAddr cur_addr = 0; + +        while (true) { +            using MemoryAttribute = Kernel::Svc::MemoryAttribute; + +            auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + +            if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || +                mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { +                const char* state = GetMemoryStateName(mem_info.state); +                const char* perm = GetMemoryPermissionString(mem_info); + +                const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; +                const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; +                const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; +                const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; + +                reply += +                    fmt::format("  {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n", +                                mem_info.base_address, mem_info.base_address + mem_info.size - 1, +                                perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); +            } + +            const uintptr_t next_address = mem_info.base_address + mem_info.size; +            if (next_address <= cur_addr) { +                break; +            } + +            cur_addr = next_address; +        } +    } else if (command_str == "help") { +        reply = "Commands:\n  get info\n  get mappings\n"; +    } else { +        reply = "Unknown command.\nCommands:\n  get info\n  get mappings\n"; +    } + +    std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()}; +    SendReply(Common::HexToString(reply_span, false)); +} +  Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {      const auto& threads{system.GlobalSchedulerContext().GetThreadList()};      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:      void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);      void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);      void HandleQuery(std::string_view command); +    void HandleRcmd(const std::vector<u8>& command);      void HandleBreakpointInsert(std::string_view command);      void HandleBreakpointRemove(std::string_view command);      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:      constexpr VAddr GetAliasCodeRegionStart() const {          return m_alias_code_region_start;      } +    constexpr VAddr GetAliasCodeRegionEnd() const { +        return m_alias_code_region_end; +    }      constexpr VAddr GetAliasCodeRegionSize() const {          return m_alias_code_region_end - m_alias_code_region_start;      } | 
