aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2025-10-27 20:55:01 +0100
committercrueter <crueter@eden-emu.dev>2025-10-27 20:55:01 +0100
commitdd9cae4ebcf4256b3da4c92e34f61fcffd85035b (patch)
tree38685fa2cd55c504326c6ee3c979c539ca5a2460
parent8fa36a7737e3fe1a53029dabff62adc827a56630 (diff)
[logging, debugger] remove unescesary logic and only query USER env variable once (#2800)
- censoring an username would lead to the variable being queried everytime something is written, just store it on a static - dont use a map<> for something that can be done in a switch statment (and that the compiler will optimise for free!!!) Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2800 Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: lizzie <lizzie@eden-emu.dev> Co-committed-by: lizzie <lizzie@eden-emu.dev>
-rw-r--r--src/common/logging/backend.cpp62
-rw-r--r--src/common/logging/text_formatter.cpp149
-rw-r--r--src/core/debugger/debugger_interface.h45
-rw-r--r--src/core/debugger/gdbstub.cpp427
-rw-r--r--src/core/debugger/gdbstub.h18
5 files changed, 267 insertions, 434 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index c2cbf3f4a5..12cf15f9cf 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -53,11 +53,8 @@ constexpr const char* TrimSourcePath(std::string_view source) {
class Backend {
public:
virtual ~Backend() = default;
-
virtual void Write(const Entry& entry) = 0;
-
virtual void EnableForStacktrace() = 0;
-
virtual void Flush() = 0;
};
@@ -65,13 +62,11 @@ public:
class ColorConsoleBackend final : public Backend {
public:
explicit ColorConsoleBackend() = default;
-
~ColorConsoleBackend() override = default;
void Write(const Entry& entry) override {
- if (enabled.load(std::memory_order_relaxed)) {
+ if (enabled.load(std::memory_order_relaxed))
PrintColoredMessage(entry);
- }
}
void Flush() override {
@@ -97,51 +92,50 @@ public:
auto old_filename = filename;
old_filename += ".old.txt";
- // Existence checks are done within the functions themselves.
- // We don't particularly care if these succeed or not.
+ // Existence checks are done within the functions themselves.
+ // We don't particularly care if these succeed or not.
static_cast<void>(FS::RemoveFile(old_filename));
static_cast<void>(FS::RenameFile(filename, old_filename));
- file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
- FS::FileType::TextFile);
+ file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
~FileBackend() override = default;
void Write(const Entry& entry) override {
- if (!enabled) {
+ if (!enabled)
return;
- }
auto message = FormatLogMessage(entry).append(1, '\n');
-
-#ifndef ANDROID
+#ifndef __ANDROID__
if (Settings::values.censor_username.GetValue()) {
- char* username = getenv("USER");
- if (!username) {
- username = getenv("USERNAME");
- }
- boost::replace_all(message, username, "user");
+ // This must be a static otherwise it would get checked on EVERY
+ // instance of logging an entry...
+ static std::string username = []() -> std::string {
+ auto* s = getenv("USER");
+ if (s == nullptr)
+ s = getenv("USERNAME");
+ return std::string{s};
+ }();
+ if (!username.empty())
+ boost::replace_all(message, username, "user");
}
#endif
-
bytes_written += file->WriteString(message);
// Option to log each line rather than 4k buffers
- if (Settings::values.log_flush_line.GetValue()) {
+ if (Settings::values.log_flush_line.GetValue())
file->Flush();
- }
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
const auto write_limit = Settings::values.extended_logging.GetValue() ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) {
- if (write_limit_exceeded) {
- // Stop writing after the write limit is exceeded.
- // Don't close the file so we can print a stacktrace if necessary
+ // Stop writing after the write limit is exceeded.
+ // Don't close the file so we can print a stacktrace if necessary
+ if (write_limit_exceeded)
enabled = false;
- }
file->Flush();
}
}
@@ -157,8 +151,8 @@ public:
private:
std::unique_ptr<FS::IOFile> file;
- bool enabled = true;
std::size_t bytes_written = 0;
+ bool enabled = true;
};
/**
@@ -209,9 +203,8 @@ bool initialization_in_progress_suppress_logging = true;
class Impl {
public:
static Impl& Instance() {
- if (!instance) {
+ if (!instance)
throw std::runtime_error("Using Logging instance before its initialization");
- }
return *instance;
}
@@ -277,25 +270,21 @@ private:
};
while (!stop_token.stop_requested()) {
message_queue.PopWait(entry, stop_token);
- if (entry.filename != nullptr) {
+ if (entry.filename != nullptr)
write_logs();
- }
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
- while (max_logs_to_write-- && message_queue.TryPop(entry)) {
+ while (max_logs_to_write-- && message_queue.TryPop(entry))
write_logs();
- }
});
}
void StopBackendThread() {
backend_thread.request_stop();
- if (backend_thread.joinable()) {
+ if (backend_thread.joinable())
backend_thread.join();
- }
-
ForEachBackend([](Backend& backend) { backend.Flush(); });
}
@@ -304,7 +293,6 @@ private:
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;
-
return {
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
.log_class = log_class,
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index 2c453177ba..17d94fe751 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -1,14 +1,17 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cstdio>
+#include <cstdint>
+#include <string>
#ifdef _WIN32
#include <windows.h>
-#endif
-
-#ifdef ANDROID
+#elif defined(__ANDROID__)
#include <android/log.h>
#endif
@@ -21,123 +24,77 @@
namespace Common::Log {
std::string FormatLogMessage(const Entry& entry) {
- unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
- unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
-
- const char* class_name = GetLogClassName(entry.log_class);
- const char* level_name = GetLevelName(entry.log_level);
-
+ auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000);
+ auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
+ char const* class_name = GetLogClassName(entry.log_class);
+ char const* level_name = GetLevelName(entry.log_level);
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
class_name, level_name, entry.filename, entry.function, entry.line_num,
entry.message);
}
void PrintMessage(const Entry& entry) {
- const auto str = FormatLogMessage(entry).append(1, '\n');
- fputs(str.c_str(), stderr);
+#ifdef _WIN32
+ auto const str = FormatLogMessage(entry).append(1, '\n');
+#else
+#define ESC "\x1b"
+ auto str = std::string{[&entry]() -> const char* {
+ switch (entry.log_level) {
+ case Level::Debug: return ESC "[0;36m"; // Cyan
+ case Level::Info: return ESC "[0;37m"; // Bright gray
+ case Level::Warning: return ESC "[1;33m"; // Bright yellow
+ case Level::Error: return ESC "[1;31m"; // Bright red
+ case Level::Critical: return ESC "[1;35m"; // Bright magenta
+ default: return ESC "[1;30m"; // Grey
+ }
+ }()};
+ str.append(FormatLogMessage(entry));
+ str.append(ESC "[0m\n");
+#undef ESC
+#endif
+ fwrite(str.c_str(), 1, str.size(), stderr);
}
void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
- if (console_handle == INVALID_HANDLE_VALUE) {
+ if (console_handle == INVALID_HANDLE_VALUE)
return;
- }
-
CONSOLE_SCREEN_BUFFER_INFO original_info = {};
GetConsoleScreenBufferInfo(console_handle, &original_info);
-
- WORD color = 0;
- switch (entry.log_level) {
- case Level::Trace: // Grey
- color = FOREGROUND_INTENSITY;
- break;
- case Level::Debug: // Cyan
- color = FOREGROUND_GREEN | FOREGROUND_BLUE;
- break;
- case Level::Info: // Bright gray
- color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
- break;
- case Level::Warning: // Bright yellow
- color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
- break;
- case Level::Error: // Bright red
- color = FOREGROUND_RED | FOREGROUND_INTENSITY;
- break;
- case Level::Critical: // Bright magenta
- color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
- break;
- case Level::Count:
- UNREACHABLE();
- }
-
+ WORD color = WORD([&entry]() {
+ switch (entry.log_level) {
+ case Level::Debug: return FOREGROUND_GREEN | FOREGROUND_BLUE; // Cyan
+ case Level::Info: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // Bright gray
+ case Level::Warning: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ case Level::Error: return FOREGROUND_RED | FOREGROUND_INTENSITY;
+ case Level::Critical: return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+ default: break;
+ }
+ return FOREGROUND_INTENSITY; // Grey
+ }());
SetConsoleTextAttribute(console_handle, color);
-#else
-#define ESC "\x1b"
- const char* color = "";
- switch (entry.log_level) {
- case Level::Trace: // Grey
- color = ESC "[1;30m";
- break;
- case Level::Debug: // Cyan
- color = ESC "[0;36m";
- break;
- case Level::Info: // Bright gray
- color = ESC "[0;37m";
- break;
- case Level::Warning: // Bright yellow
- color = ESC "[1;33m";
- break;
- case Level::Error: // Bright red
- color = ESC "[1;31m";
- break;
- case Level::Critical: // Bright magenta
- color = ESC "[1;35m";
- break;
- case Level::Count:
- UNREACHABLE();
- }
-
- fputs(color, stderr);
#endif
-
PrintMessage(entry);
-
#ifdef _WIN32
SetConsoleTextAttribute(console_handle, original_info.wAttributes);
-#else
- fputs(ESC "[0m", stderr);
-#undef ESC
#endif
}
void PrintMessageToLogcat(const Entry& entry) {
#ifdef ANDROID
- const auto str = FormatLogMessage(entry);
-
- android_LogPriority android_log_priority;
- switch (entry.log_level) {
- case Level::Trace:
- android_log_priority = ANDROID_LOG_VERBOSE;
- break;
- case Level::Debug:
- android_log_priority = ANDROID_LOG_DEBUG;
- break;
- case Level::Info:
- android_log_priority = ANDROID_LOG_INFO;
- break;
- case Level::Warning:
- android_log_priority = ANDROID_LOG_WARN;
- break;
- case Level::Error:
- android_log_priority = ANDROID_LOG_ERROR;
- break;
- case Level::Critical:
- android_log_priority = ANDROID_LOG_FATAL;
- break;
- case Level::Count:
- UNREACHABLE();
- }
+ android_LogPriority android_log_priority = [&]() {
+ switch (entry.log_level) {
+ case Level::Debug: return ANDROID_LOG_DEBUG;
+ case Level::Info: return ANDROID_LOG_INFO;
+ case Level::Warning: return ANDROID_LOG_WARN;
+ case Level::Error: return ANDROID_LOG_ERROR;
+ case Level::Critical: return ANDROID_LOG_FATAL;
+ case Level::Count:
+ case Level::Trace: return ANDROID_LOG_VERBOSE;
+ }
+ }();
+ auto const str = FormatLogMessage(entry);
__android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str());
#endif
}
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
index 5b31edc430..4e18749249 100644
--- a/src/core/debugger/debugger_interface.h
+++ b/src/core/debugger/debugger_interface.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -28,26 +31,18 @@ class DebuggerBackend {
public:
virtual ~DebuggerBackend() = default;
- /**
- * Can be invoked from a callback to synchronously wait for more data.
- * Will return as soon as least one byte is received. Reads up to 4096 bytes.
- */
+ /// Can be invoked from a callback to synchronously wait for more data.
+ /// Will return as soon as least one byte is received. Reads up to 4096 bytes.
virtual std::span<const u8> ReadFromClient() = 0;
- /**
- * Can be invoked from a callback to write data to the client.
- * Returns immediately after the data is sent.
- */
+ /// Can be invoked from a callback to write data to the client.
+ /// Returns immediately after the data is sent.
virtual void WriteToClient(std::span<const u8> data) = 0;
- /**
- * Gets the currently active thread when the debugger is stopped.
- */
+ /// Gets the currently active thread when the debugger is stopped.
virtual Kernel::KThread* GetActiveThread() = 0;
- /**
- * Sets the currently active thread when the debugger is stopped.
- */
+ /// Sets the currently active thread when the debugger is stopped.
virtual void SetActiveThread(Kernel::KThread* thread) = 0;
};
@@ -57,30 +52,20 @@ public:
virtual ~DebuggerFrontend() = default;
- /**
- * Called after the client has successfully connected to the port.
- */
+ /// Called after the client has successfully connected to the port.
virtual void Connected() = 0;
- /**
- * Called when emulation has stopped.
- */
+ /// Called when emulation has stopped.
virtual void Stopped(Kernel::KThread* thread) = 0;
- /**
- * Called when emulation is shutting down.
- */
+ /// Called when emulation is shutting down.
virtual void ShuttingDown() = 0;
- /*
- * Called when emulation has stopped on a watchpoint.
- */
+ /// Called when emulation has stopped on a watchpoint.
virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
- /**
- * Called when new data is asynchronously received on the client socket.
- * A list of actions to perform is returned.
- */
+ /// Called when new data is asynchronously received on the client socket.
+ /// A list of actions to perform is returned.
[[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
protected:
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 16c42bf175..a23ef2d65f 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -49,28 +49,14 @@ static u8 CalculateChecksum(std::string_view data) {
static std::string EscapeGDB(std::string_view data) {
std::string escaped;
- escaped.reserve(data.size());
-
- for (char c : data) {
+ for (char const c : data)
switch (c) {
- case '#':
- escaped += "}\x03";
- break;
- case '$':
- escaped += "}\x04";
- break;
- case '*':
- escaped += "}\x0a";
- break;
- case '}':
- escaped += "}\x5d";
- break;
- default:
- escaped += c;
- break;
+ case '#': escaped += "}\x03"; break;
+ case '$': escaped += "}\x04"; break;
+ case '*': escaped += "}\x0a"; break;
+ case '}': escaped += "}\x5d"; break;
+ default: escaped += c; break;
}
- }
-
return escaped;
}
@@ -82,38 +68,26 @@ static std::string EscapeXML(std::string_view data) {
}
std::string escaped;
- escaped.reserve(data.size());
-
- for (char32_t c : converted) {
+ for (char32_t const c : converted)
switch (c) {
- case '&':
- escaped += "&amp;";
- break;
- case '"':
- escaped += "&quot;";
- break;
- case '<':
- escaped += "&lt;";
- break;
- case '>':
- escaped += "&gt;";
- break;
+ case '&': escaped += "&amp;"; break;
+ case '"': escaped += "&quot;"; break;
+ case '<': escaped += "&lt;"; break;
+ case '>': escaped += "&gt;"; break;
default:
if (c > 0x7f) {
- escaped += fmt::format("&#{};", static_cast<u32>(c));
+ escaped += fmt::format("&#{};", u32(c));
} else {
- escaped += static_cast<char>(c);
+ escaped += char(c);
}
break;
}
- }
-
return escaped;
}
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
- if (GetProcess()->Is64Bit()) {
+ if (debug_process->Is64Bit()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
@@ -148,56 +122,42 @@ void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint&
}
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
- std::vector<DebuggerAction> actions;
current_command.insert(current_command.end(), data.begin(), data.end());
-
- while (current_command.size() != 0) {
+ std::vector<DebuggerAction> actions;
+ while (!current_command.empty())
ProcessData(actions);
- }
-
return actions;
}
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
- const char c{current_command[0]};
-
+ const char c = current_command[0];
// Acknowledgement
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
current_command.erase(current_command.begin());
- return;
- }
-
// Interrupt
- if (c == GDB_STUB_INT3) {
+ } else if (c == GDB_STUB_INT3) {
LOG_INFO(Debug_GDBStub, "Received interrupt");
current_command.erase(current_command.begin());
actions.push_back(DebuggerAction::Interrupt);
SendStatus(GDB_STUB_ACK);
- return;
- }
-
// Otherwise, require the data to be the start of a command
- if (c != GDB_STUB_START) {
+ } else if (c != GDB_STUB_START) {
LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
current_command.clear();
SendStatus(GDB_STUB_NACK);
- return;
- }
-
- // Continue reading until command is complete
- while (CommandEnd() == current_command.end()) {
- const auto new_data{backend.ReadFromClient()};
- current_command.insert(current_command.end(), new_data.begin(), new_data.end());
- }
-
- // Execute and respond to GDB
- const auto command{DetachCommand()};
-
- if (command) {
- SendStatus(GDB_STUB_ACK);
- ExecuteCommand(*command, actions);
} else {
- SendStatus(GDB_STUB_NACK);
+ // Continue reading until command is complete
+ while (CommandEnd() == current_command.end()) {
+ const auto new_data{backend.ReadFromClient()};
+ current_command.insert(current_command.end(), new_data.begin(), new_data.end());
+ }
+ // Execute and respond to GDB
+ if (auto const cmd = DetachCommand(); cmd) {
+ SendStatus(GDB_STUB_ACK);
+ ExecuteCommand(*cmd, actions);
+ } else {
+ SendStatus(GDB_STUB_NACK);
+ }
}
}
@@ -207,9 +167,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
if (packet.length() == 0) {
SendReply(GDB_STUB_REPLY_ERR);
return;
- }
-
- if (packet.starts_with("vCont")) {
+ } else if (packet.starts_with("vCont")) {
HandleVCont(packet.substr(5), actions);
return;
}
@@ -218,14 +176,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
switch (packet[0]) {
case 'H': {
- Kernel::KThread* thread{nullptr};
- s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
- if (thread_id >= 1) {
- thread = GetThreadByID(thread_id);
- } else {
- thread = backend.GetActiveThread();
- }
-
+ s64 thread_id = strtoll(command.data() + 1, nullptr, 16);
+ Kernel::KThread* thread = thread_id >= 1 ? GetThreadByID(thread_id) : backend.GetActiveThread();
if (thread) {
SendReply(GDB_STUB_REPLY_OK);
backend.SetActiveThread(thread);
@@ -235,7 +187,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
break;
}
case 'T': {
- s64 thread_id{strtoll(command.data(), nullptr, 16)};
+ s64 thread_id = strtoll(command.data(), nullptr, 16);
if (GetThreadByID(thread_id)) {
SendReply(GDB_STUB_REPLY_OK);
} else {
@@ -262,27 +214,26 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
SendReply(GDB_STUB_REPLY_OK);
break;
case 'p': {
- const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ const size_t reg = size_t(strtoll(command.data(), nullptr, 16));
SendReply(arch->RegRead(backend.GetActiveThread(), reg));
break;
}
case 'P': {
- const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
- const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ const auto sep = std::find(command.begin(), command.end(), '=') - command.begin() + 1;
+ const size_t reg = size_t(strtoll(command.data(), nullptr, 16));
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
SendReply(GDB_STUB_REPLY_OK);
break;
}
case 'm': {
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
- const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
- const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
+ const size_t addr = size_t(strtoll(command.data(), nullptr, 16));
+ const size_t size = size_t(strtoll(command.data() + sep, nullptr, 16));
std::vector<u8> mem(size);
- if (GetMemory().ReadBlock(addr, mem.data(), size)) {
+ if (debug_process->GetMemory().ReadBlock(addr, mem.data(), size)) {
// Restore any bytes belonging to replaced instructions.
- auto it = replaced_instructions.lower_bound(addr);
- for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
+ for (auto it = replaced_instructions.lower_bound(addr); it != replaced_instructions.end() && it->first < addr + size; it++) {
// Get the bytes of the instruction we previously replaced.
const u32 original_bytes = it->second;
@@ -307,14 +258,14 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
- const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
- const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+ const size_t addr{size_t(strtoll(command.data(), nullptr, 16))};
+ const size_t size{size_t(strtoll(command.data() + size_sep, nullptr, 16))};
const auto mem_substr{std::string_view(command).substr(mem_sep)};
const auto mem{Common::HexStringToVector(mem_substr, false)};
- if (GetMemory().WriteBlock(addr, mem.data(), size)) {
- Core::InvalidateInstructionCacheRange(GetProcess(), addr, size);
+ if (debug_process->GetMemory().WriteBlock(addr, mem.data(), size)) {
+ Core::InvalidateInstructionCacheRange(debug_process, addr, size);
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
@@ -349,14 +300,13 @@ enum class BreakpointType {
};
void GDBStub::HandleBreakpointInsert(std::string_view command) {
- const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
- const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
- const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
- command.begin() + 1};
- const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
- const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
-
- if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
+ const auto type = BreakpointType(strtoll(command.data(), nullptr, 16));
+ const auto addr_sep = std::find(command.begin(), command.end(), ',') - command.begin() + 1;
+ const auto size_sep = std::find(command.begin() + addr_sep, command.end(), ',') - command.begin() + 1;
+ const size_t addr = size_t(strtoll(command.data() + addr_sep, nullptr, 16));
+ const size_t size = size_t(strtoll(command.data() + size_sep, nullptr, 16));
+
+ if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
@@ -365,20 +315,19 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
switch (type) {
case BreakpointType::Software:
- replaced_instructions[addr] = GetMemory().Read32(addr);
- GetMemory().Write32(addr, arch->BreakpointInstruction());
- Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
+ replaced_instructions[addr] = debug_process->GetMemory().Read32(addr);
+ debug_process->GetMemory().Write32(addr, arch->BreakpointInstruction());
+ Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32));
success = true;
break;
case BreakpointType::WriteWatch:
- success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
+ success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
- success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
+ success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
- success =
- GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
@@ -393,41 +342,37 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
}
}
-void GDBStub::HandleBreakpointRemove(std::string_view command) {
- const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
- const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
- const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
- command.begin() + 1};
- const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
- const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+void GDBStub::HandleBreakpointRemove(std::string_view sv) {
+ const auto type = BreakpointType(strtoll(sv.data(), nullptr, 16));
+ const auto addr_sep = std::find(sv.begin(), sv.end(), ',') - sv.begin() + 1;
+ const auto size_sep = std::find(sv.begin() + addr_sep, sv.end(), ',') - sv.begin() + 1;
+ const size_t addr = size_t(strtoll(sv.data() + addr_sep, nullptr, 16));
+ const size_t size = size_t(strtoll(sv.data() + size_sep, nullptr, 16));
- if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
+ if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
- bool success{};
-
+ bool success = false;
switch (type) {
case BreakpointType::Software: {
- const auto orig_insn{replaced_instructions.find(addr)};
- if (orig_insn != replaced_instructions.end()) {
- GetMemory().Write32(addr, orig_insn->second);
- Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
+ if (auto const orig_insn = replaced_instructions.find(addr); orig_insn != replaced_instructions.end()) {
+ debug_process->GetMemory().Write32(addr, orig_insn->second);
+ Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32));
replaced_instructions.erase(addr);
success = true;
}
break;
}
case BreakpointType::WriteWatch:
- success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
+ success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
- success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
+ success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
- success =
- GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
@@ -454,22 +399,21 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
}
}
-void GDBStub::HandleQuery(std::string_view command) {
- if (command.starts_with("TStatus")) {
+void GDBStub::HandleQuery(std::string_view sv) {
+ if (sv.starts_with("TStatus")) {
// no tracepoint support
SendReply("T0");
- } else if (command.starts_with("Supported")) {
+ } else if (sv.starts_with("Supported")) {
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
"vContSupported+;QStartNoAckMode+");
- } else if (command.starts_with("Xfer:features:read:target.xml:")) {
+ } else if (sv.starts_with("Xfer:features:read:target.xml:")) {
const auto target_xml{arch->GetTargetXML()};
- SendReply(PaginateBuffer(target_xml, command.substr(30)));
- } else if (command.starts_with("Offsets")) {
- const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess());
+ SendReply(PaginateBuffer(target_xml, sv.substr(30)));
+ } else if (sv.starts_with("Offsets")) {
+ const auto main_offset = Core::FindMainModuleEntrypoint(debug_process);
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
- } else if (command.starts_with("Xfer:libraries:read::")) {
- auto modules = Core::FindModules(GetProcess());
-
+ } else if (sv.starts_with("Xfer:libraries:read::")) {
+ auto modules = Core::FindModules(debug_process);
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
buffer += "<library-list>";
@@ -479,24 +423,23 @@ void GDBStub::HandleQuery(std::string_view command) {
}
buffer += "</library-list>";
- SendReply(PaginateBuffer(buffer, command.substr(21)));
- } else if (command.starts_with("fThreadInfo")) {
+ SendReply(PaginateBuffer(buffer, sv.substr(21)));
+ } else if (sv.starts_with("fThreadInfo")) {
// beginning of list
- const auto& threads = GetProcess()->GetThreadList();
+ const auto& threads = debug_process->GetThreadList();
std::vector<std::string> thread_ids;
- for (const auto& thread : threads) {
+ for (const auto& thread : threads)
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
- }
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
- } else if (command.starts_with("sThreadInfo")) {
+ } else if (sv.starts_with("sThreadInfo")) {
// end of list
SendReply("l");
- } else if (command.starts_with("Xfer:threads:read::")) {
+ } else if (sv.starts_with("Xfer:threads:read::")) {
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
buffer += "<threads>";
- const auto& threads = GetProcess()->GetThreadList();
+ const auto& threads = debug_process->GetThreadList();
for (const auto& thread : threads) {
auto thread_name{Core::GetThreadName(&thread)};
if (!thread_name) {
@@ -510,109 +453,95 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += "</threads>";
- SendReply(PaginateBuffer(buffer, command.substr(19)));
- } else if (command.starts_with("Attached")) {
+ SendReply(PaginateBuffer(buffer, sv.substr(19)));
+ } else if (sv.starts_with("Attached")) {
SendReply("0");
- } else if (command.starts_with("StartNoAckMode")) {
+ } else if (sv.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 if (sv.starts_with("Rcmd,")) {
+ HandleRcmd(Common::HexStringToVector(sv.substr(5), false));
} else {
SendReply(GDB_STUB_REPLY_EMPTY);
}
}
-void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
- if (command == "?") {
- // Continuing and stepping are supported
- // (signal is ignored, but required for GDB to use vCont)
+void GDBStub::HandleVCont(std::string_view sv, std::vector<DebuggerAction>& actions) {
+ // Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
+ if (sv == "?") {
SendReply("vCont;c;C;s;S");
- return;
- }
-
- Kernel::KThread* stepped_thread{nullptr};
- bool lock_execution{true};
-
- std::vector<std::string> entries;
- boost::split(entries, command.substr(1), boost::is_any_of(";"));
- for (const auto& thread_action : entries) {
- std::vector<std::string> parts;
- boost::split(parts, thread_action, boost::is_any_of(":"));
-
- if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
- lock_execution = false;
- }
- if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
- stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
+ } else {
+ Kernel::KThread* stepped_thread = nullptr;
+ bool lock_execution = true;
+ std::vector<std::string> entries;
+ boost::split(entries, sv.substr(1), boost::is_any_of(";"));
+ for (auto const& thread_action : entries) {
+ std::vector<std::string> parts;
+ boost::split(parts, thread_action, boost::is_any_of(":"));
+ if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C")))
+ lock_execution = false;
+ if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S")))
+ stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
}
- }
- if (stepped_thread) {
- backend.SetActiveThread(stepped_thread);
- actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
- : DebuggerAction::StepThreadUnlocked);
- } else {
- actions.push_back(DebuggerAction::Continue);
+ if (stepped_thread) {
+ backend.SetActiveThread(stepped_thread);
+ actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked : DebuggerAction::StepThreadUnlocked);
+ } else {
+ actions.push_back(DebuggerAction::Continue);
+ }
}
}
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
- 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},
- {"Transferred ", Kernel::Svc::MemoryState::Transferred},
- {"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
- {"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},
- }};
- for (size_t i = 0; i < MemoryStateNames.size(); i++) {
- if (std::get<1>(MemoryStateNames[i]) == state) {
- return std::get<0>(MemoryStateNames[i]);
- }
+#define MEMORY_STATE_LIST \
+ MEMORY_STATE_ELEM(Free) \
+ MEMORY_STATE_ELEM(Io) \
+ MEMORY_STATE_ELEM(Static) \
+ MEMORY_STATE_ELEM(Code) \
+ MEMORY_STATE_ELEM(CodeData) \
+ MEMORY_STATE_ELEM(Normal) \
+ MEMORY_STATE_ELEM(Shared) \
+ MEMORY_STATE_ELEM(AliasCode) \
+ MEMORY_STATE_ELEM(AliasCodeData) \
+ MEMORY_STATE_ELEM(Ipc) \
+ MEMORY_STATE_ELEM(Stack) \
+ MEMORY_STATE_ELEM(ThreadLocal) \
+ MEMORY_STATE_ELEM(Transferred) \
+ MEMORY_STATE_ELEM(SharedTransferred) \
+ MEMORY_STATE_ELEM(SharedCode) \
+ MEMORY_STATE_ELEM(Inaccessible) \
+ MEMORY_STATE_ELEM(NonSecureIpc) \
+ MEMORY_STATE_ELEM(NonDeviceIpc) \
+ MEMORY_STATE_ELEM(Kernel) \
+ MEMORY_STATE_ELEM(GeneratedCode) \
+ MEMORY_STATE_ELEM(CodeOut) \
+ MEMORY_STATE_ELEM(Coverage)
+ switch (state) {
+#define MEMORY_STATE_ELEM(elem) case Kernel::Svc::MemoryState::elem: return #elem;
+ MEMORY_STATE_LIST
+#undef MEMORY_STATE_LIST
+ default: return "Unknown";
}
- 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 "---";
+ } else {
+ 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 "---";
+ }
}
}
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 = GetProcess();
- auto& page_table = process->GetPageTable();
+ auto& page_table = debug_process->GetPageTable();
if (command_str == "fastmem" || command_str == "get fastmem") {
if (Settings::IsFastmemEnabled()) {
const auto& impl = page_table.GetImpl();
@@ -627,11 +556,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = "Fastmem is not enabled.\n";
}
} else if (command_str == "info" || command_str == "get info") {
- auto modules = Core::FindModules(process);
+ auto modules = Core::FindModules(debug_process);
reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n",
- process->GetProcessId(), process->GetName(), process->GetProgramId());
+ debug_process->GetProcessId(),
+ debug_process->GetName(),
+ debug_process->GetProgramId());
reply += fmt::format(
"Layout:\n"
" Alias: {:#012x} - {:#012x}\n"
@@ -648,10 +579,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
GetInteger(page_table.GetStackRegionStart()),
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
- for (const auto& [vaddr, name] : modules) {
- reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
- GetInteger(Core::GetModuleEnd(process, vaddr)), name);
- }
+ for (const auto& [vaddr, name] : modules)
+ reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, GetInteger(Core::GetModuleEnd(debug_process, vaddr)), name);
} else if (command_str == "mappings" || command_str == "get mappings") {
reply = "Mappings:\n";
VAddr cur_addr = 0;
@@ -683,10 +612,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
- if (next_address <= cur_addr) {
+ if (next_address <= cur_addr)
break;
- }
-
cur_addr = next_address;
}
} else {
@@ -698,20 +625,16 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
- auto& threads{GetProcess()->GetThreadList()};
- for (auto& thread : threads) {
- if (thread.GetThreadId() == thread_id) {
+ auto& threads = debug_process->GetThreadList();
+ for (auto& thread : threads)
+ if (thread.GetThreadId() == thread_id)
return std::addressof(thread);
- }
- }
-
return nullptr;
}
std::vector<char>::const_iterator GDBStub::CommandEnd() const {
// Find the end marker
- const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
-
+ const auto end = std::find(current_command.begin(), current_command.end(), GDB_STUB_END);
// Require the checksum to be present
return (std::min)(end + 2, current_command.end());
}
@@ -737,8 +660,7 @@ std::optional<std::string> GDBStub::DetachCommand() {
// Verify checksum
if (calculated != received) {
- LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
- calculated, received);
+ LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", calculated, received);
return std::nullopt;
}
@@ -746,9 +668,8 @@ std::optional<std::string> GDBStub::DetachCommand() {
}
void GDBStub::SendReply(std::string_view data) {
- const auto escaped{EscapeGDB(data)};
- const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
- CalculateChecksum(escaped))};
+ const auto escaped = EscapeGDB(data);
+ const auto output = fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, CalculateChecksum(escaped));
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
// C++ string support is complete rubbish
@@ -758,21 +679,11 @@ void GDBStub::SendReply(std::string_view data) {
}
void GDBStub::SendStatus(char status) {
- if (no_ack) {
- return;
+ if (!no_ack) {
+ std::array<u8, 1> buf = {u8(status)};
+ LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
+ backend.WriteToClient(buf);
}
-
- std::array<u8, 1> buf = {static_cast<u8>(status)};
- LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
- backend.WriteToClient(buf);
-}
-
-Kernel::KProcess* GDBStub::GetProcess() {
- return debug_process;
-}
-
-Core::Memory::Memory& GDBStub::GetMemory() {
- return GetProcess()->GetMemory();
}
} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 232dcf49f5..140b0e8e25 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -24,19 +27,14 @@ namespace Core {
class System;
-class GDBStub : public DebuggerFrontend {
-public:
- explicit GDBStub(DebuggerBackend& backend, Core::System& system,
- Kernel::KProcess* debug_process);
+struct GDBStub : public DebuggerFrontend {
+ explicit GDBStub(DebuggerBackend& backend, Core::System& system, Kernel::KProcess* debug_process);
~GDBStub() override;
-
void Connected() override;
void Stopped(Kernel::KThread* thread) override;
void ShuttingDown() override;
void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
-
-private:
void ProcessData(std::vector<DebuggerAction>& actions);
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
@@ -47,14 +45,8 @@ private:
std::vector<char>::const_iterator CommandEnd() const;
std::optional<std::string> DetachCommand();
Kernel::KThread* GetThreadByID(u64 thread_id);
-
void SendReply(std::string_view data);
void SendStatus(char status);
-
- Kernel::KProcess* GetProcess();
- Core::Memory::Memory& GetMemory();
-
-private:
Core::System& system;
Kernel::KProcess* debug_process;
std::unique_ptr<GDBStubArch> arch;