forked from suyu/suyu
Merge pull request #9225 from liamwhite/debugger-instance
Debugger improvements
This commit is contained in:
commit
040a01a5dd
4 changed files with 254 additions and 74 deletions
|
@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
|
||||||
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
|
||||||
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
|
||||||
c(received_data);
|
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>
|
template <typename Readable, typename Buffer>
|
||||||
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
|
||||||
static_assert(std::is_trivial_v<Buffer>);
|
static_assert(std::is_trivial_v<Buffer>);
|
||||||
|
@ -59,9 +68,7 @@ namespace Core {
|
||||||
|
|
||||||
class DebuggerImpl : public DebuggerBackend {
|
class DebuggerImpl : public DebuggerBackend {
|
||||||
public:
|
public:
|
||||||
explicit DebuggerImpl(Core::System& system_, u16 port)
|
explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
|
||||||
: system{system_}, signal_pipe{io_context}, client_socket{io_context} {
|
|
||||||
frontend = std::make_unique<GDBStub>(*this, system);
|
|
||||||
InitializeServer(port);
|
InitializeServer(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,39 +77,42 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SignalDebugger(SignalInfo signal_info) {
|
bool SignalDebugger(SignalInfo signal_info) {
|
||||||
{
|
std::scoped_lock lk{connection_lock};
|
||||||
std::scoped_lock lk{connection_lock};
|
|
||||||
|
|
||||||
if (stopped) {
|
if (stopped || !state) {
|
||||||
// Do not notify the debugger about another event.
|
// Do not notify the debugger about another event.
|
||||||
// It should be ignored.
|
// It should be ignored.
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the state.
|
|
||||||
stopped = true;
|
|
||||||
info = signal_info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up the state.
|
||||||
|
stopped = true;
|
||||||
|
state->info = signal_info;
|
||||||
|
|
||||||
// Write a single byte into the pipe to wake up the debug interface.
|
// 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;
|
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 {
|
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 {
|
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 {
|
void SetActiveThread(Kernel::KThread* thread) override {
|
||||||
active_thread = thread;
|
state->active_thread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KThread* GetActiveThread() override {
|
Kernel::KThread* GetActiveThread() override {
|
||||||
return active_thread;
|
return state->active_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -113,65 +123,78 @@ private:
|
||||||
|
|
||||||
// Run the connection thread.
|
// Run the connection thread.
|
||||||
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
connection_thread = std::jthread([&, port](std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("Debugger");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialize the listening socket and accept a new client.
|
// Initialize the listening socket and accept a new client.
|
||||||
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
|
||||||
tcp::acceptor acceptor{io_context, endpoint};
|
tcp::acceptor acceptor{io_context, endpoint};
|
||||||
|
|
||||||
acceptor.async_accept(client_socket, [](const auto&) {});
|
AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
|
||||||
io_context.run_one();
|
|
||||||
io_context.restart();
|
|
||||||
|
|
||||||
if (stop_token.stop_requested()) {
|
while (!stop_token.stop_requested() && io_context.run()) {
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadLoop(stop_token);
|
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
|
||||||
|
LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
|
||||||
|
|
||||||
|
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(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();
|
||||||
|
}
|
||||||
|
|
||||||
void ShutdownServer() {
|
void ShutdownServer() {
|
||||||
connection_thread.request_stop();
|
connection_thread.request_stop();
|
||||||
io_context.stop();
|
io_context.stop();
|
||||||
connection_thread.join();
|
connection_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadLoop(std::stop_token stop_token) {
|
|
||||||
Common::SetCurrentThreadName("Debugger");
|
|
||||||
|
|
||||||
// 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); });
|
|
||||||
|
|
||||||
// Set the active thread.
|
|
||||||
UpdateActiveThread();
|
|
||||||
|
|
||||||
// Set up the frontend.
|
|
||||||
frontend->Connected();
|
|
||||||
|
|
||||||
// Main event loop.
|
|
||||||
while (!stop_token.stop_requested() && io_context.run()) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipeData(std::span<const u8> data) {
|
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::Stopped:
|
||||||
case SignalType::Watchpoint:
|
case SignalType::Watchpoint:
|
||||||
// Stop emulation.
|
// Stop emulation.
|
||||||
PauseEmulation();
|
PauseEmulation();
|
||||||
|
|
||||||
// Notify the client.
|
// Notify the client.
|
||||||
active_thread = info.thread;
|
state->active_thread = state->info.thread;
|
||||||
UpdateActiveThread();
|
UpdateActiveThread();
|
||||||
|
|
||||||
if (info.type == SignalType::Watchpoint) {
|
if (state->info.type == SignalType::Watchpoint) {
|
||||||
frontend->Watchpoint(active_thread, *info.watchpoint);
|
frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
|
||||||
} else {
|
} else {
|
||||||
frontend->Stopped(active_thread);
|
frontend->Stopped(state->active_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -179,8 +202,8 @@ private:
|
||||||
frontend->ShuttingDown();
|
frontend->ShuttingDown();
|
||||||
|
|
||||||
// Wait for emulation to shut down gracefully now.
|
// Wait for emulation to shut down gracefully now.
|
||||||
signal_pipe.close();
|
state->signal_pipe.close();
|
||||||
client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
|
||||||
LOG_INFO(Debug_GDBStub, "Shut down server");
|
LOG_INFO(Debug_GDBStub, "Shut down server");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -188,17 +211,16 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientData(std::span<const u8> data) {
|
void ClientData(std::span<const u8> data) {
|
||||||
|
std::scoped_lock lk{connection_lock};
|
||||||
|
|
||||||
const auto actions{frontend->ClientData(data)};
|
const auto actions{frontend->ClientData(data)};
|
||||||
for (const auto action : actions) {
|
for (const auto action : actions) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case DebuggerAction::Interrupt: {
|
case DebuggerAction::Interrupt: {
|
||||||
{
|
stopped = true;
|
||||||
std::scoped_lock lk{connection_lock};
|
|
||||||
stopped = true;
|
|
||||||
}
|
|
||||||
PauseEmulation();
|
PauseEmulation();
|
||||||
UpdateActiveThread();
|
UpdateActiveThread();
|
||||||
frontend->Stopped(active_thread);
|
frontend->Stopped(state->active_thread);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DebuggerAction::Continue:
|
case DebuggerAction::Continue:
|
||||||
|
@ -206,15 +228,15 @@ private:
|
||||||
break;
|
break;
|
||||||
case DebuggerAction::StepThreadUnlocked:
|
case DebuggerAction::StepThreadUnlocked:
|
||||||
MarkResumed([&] {
|
MarkResumed([&] {
|
||||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||||
ResumeEmulation(active_thread);
|
ResumeEmulation(state->active_thread);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case DebuggerAction::StepThreadLocked: {
|
case DebuggerAction::StepThreadLocked: {
|
||||||
MarkResumed([&] {
|
MarkResumed([&] {
|
||||||
active_thread->SetStepState(Kernel::StepState::StepPending);
|
state->active_thread->SetStepState(Kernel::StepState::StepPending);
|
||||||
active_thread->Resume(Kernel::SuspendType::Debug);
|
state->active_thread->Resume(Kernel::SuspendType::Debug);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -254,15 +276,14 @@ private:
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
void MarkResumed(Callback&& cb) {
|
void MarkResumed(Callback&& cb) {
|
||||||
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
Kernel::KScopedSchedulerLock sl{system.Kernel()};
|
||||||
std::scoped_lock cl{connection_lock};
|
|
||||||
stopped = false;
|
stopped = false;
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateActiveThread() {
|
void UpdateActiveThread() {
|
||||||
const auto& threads{ThreadList()};
|
const auto& threads{ThreadList()};
|
||||||
if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
|
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
|
||||||
active_thread = threads[0];
|
state->active_thread = threads[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,18 +295,22 @@ private:
|
||||||
System& system;
|
System& system;
|
||||||
std::unique_ptr<DebuggerFrontend> frontend;
|
std::unique_ptr<DebuggerFrontend> frontend;
|
||||||
|
|
||||||
|
boost::asio::io_context io_context;
|
||||||
std::jthread connection_thread;
|
std::jthread connection_thread;
|
||||||
std::mutex connection_lock;
|
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;
|
struct ConnectionState {
|
||||||
Kernel::KThread* active_thread;
|
boost::asio::ip::tcp::socket client_socket;
|
||||||
bool pipe_data;
|
boost::process::async_pipe signal_pipe;
|
||||||
bool stopped;
|
|
||||||
|
|
||||||
std::array<u8, 4096> client_data;
|
SignalInfo info;
|
||||||
|
Kernel::KThread* active_thread;
|
||||||
|
std::array<u8, 4096> client_data;
|
||||||
|
bool pipe_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<ConnectionState> state{};
|
||||||
|
bool stopped{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Debugger::Debugger(Core::System& system, u16 port) {
|
Debugger::Debugger(Core::System& system, u16 port) {
|
||||||
|
|
|
@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
|
||||||
} else if (command.starts_with("StartNoAckMode")) {
|
} else if (command.starts_with("StartNoAckMode")) {
|
||||||
no_ack = true;
|
no_ack = true;
|
||||||
SendReply(GDB_STUB_REPLY_OK);
|
SendReply(GDB_STUB_REPLY_OK);
|
||||||
|
} else if (command.starts_with("Rcmd,")) {
|
||||||
|
HandleRcmd(Common::HexStringToVector(command.substr(5), false));
|
||||||
} else {
|
} else {
|
||||||
SendReply(GDB_STUB_REPLY_EMPTY);
|
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) {
|
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
|
||||||
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
|
const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
|
||||||
for (auto* thread : threads) {
|
for (auto* thread : threads) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ private:
|
||||||
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
|
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
|
||||||
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
|
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
|
||||||
void HandleQuery(std::string_view command);
|
void HandleQuery(std::string_view command);
|
||||||
|
void HandleRcmd(const std::vector<u8>& command);
|
||||||
void HandleBreakpointInsert(std::string_view command);
|
void HandleBreakpointInsert(std::string_view command);
|
||||||
void HandleBreakpointRemove(std::string_view command);
|
void HandleBreakpointRemove(std::string_view command);
|
||||||
std::vector<char>::const_iterator CommandEnd() const;
|
std::vector<char>::const_iterator CommandEnd() const;
|
||||||
|
|
|
@ -320,6 +320,9 @@ public:
|
||||||
constexpr VAddr GetAliasCodeRegionStart() const {
|
constexpr VAddr GetAliasCodeRegionStart() const {
|
||||||
return m_alias_code_region_start;
|
return m_alias_code_region_start;
|
||||||
}
|
}
|
||||||
|
constexpr VAddr GetAliasCodeRegionEnd() const {
|
||||||
|
return m_alias_code_region_end;
|
||||||
|
}
|
||||||
constexpr VAddr GetAliasCodeRegionSize() const {
|
constexpr VAddr GetAliasCodeRegionSize() const {
|
||||||
return m_alias_code_region_end - m_alias_code_region_start;
|
return m_alias_code_region_end - m_alias_code_region_start;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue