[logging, debugger] remove unescesary logic and only query USER env variable once

Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie 2025-10-20 19:19:49 +00:00 committed by crueter
parent e33d426ac4
commit 87c0994a0d
5 changed files with 257 additions and 434 deletions

View file

@ -53,11 +53,8 @@ constexpr const char* TrimSourcePath(std::string_view source) {
class Backend { class Backend {
public: public:
virtual ~Backend() = default; virtual ~Backend() = default;
virtual void Write(const Entry& entry) = 0; virtual void Write(const Entry& entry) = 0;
virtual void EnableForStacktrace() = 0; virtual void EnableForStacktrace() = 0;
virtual void Flush() = 0; virtual void Flush() = 0;
}; };
@ -65,13 +62,11 @@ public:
class ColorConsoleBackend final : public Backend { class ColorConsoleBackend final : public Backend {
public: public:
explicit ColorConsoleBackend() = default; explicit ColorConsoleBackend() = default;
~ColorConsoleBackend() override = default; ~ColorConsoleBackend() override = default;
void Write(const Entry& entry) override { void Write(const Entry& entry) override {
if (enabled.load(std::memory_order_relaxed)) { if (enabled.load(std::memory_order_relaxed))
PrintColoredMessage(entry); PrintColoredMessage(entry);
}
} }
void Flush() override { void Flush() override {
@ -97,51 +92,50 @@ public:
auto old_filename = filename; auto old_filename = filename;
old_filename += ".old.txt"; old_filename += ".old.txt";
// Existence checks are done within the functions themselves. // Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not. // We don't particularly care if these succeed or not.
static_cast<void>(FS::RemoveFile(old_filename)); static_cast<void>(FS::RemoveFile(old_filename));
static_cast<void>(FS::RenameFile(filename, old_filename)); static_cast<void>(FS::RenameFile(filename, old_filename));
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
FS::FileType::TextFile);
} }
~FileBackend() override = default; ~FileBackend() override = default;
void Write(const Entry& entry) override { void Write(const Entry& entry) override {
if (!enabled) { if (!enabled)
return; return;
}
auto message = FormatLogMessage(entry).append(1, '\n'); auto message = FormatLogMessage(entry).append(1, '\n');
#ifndef __ANDROID__
#ifndef ANDROID
if (Settings::values.censor_username.GetValue()) { if (Settings::values.censor_username.GetValue()) {
char* username = getenv("USER"); // This must be a static otherwise it would get checked on EVERY
if (!username) { // instance of logging an entry...
username = getenv("USERNAME"); static std::string username = []() -> std::string {
} auto* s = getenv("USER");
boost::replace_all(message, username, "user"); if (s == nullptr)
s = getenv("USERNAME");
return std::string{s};
}();
if (!username.empty())
boost::replace_all(message, username, "user");
} }
#endif #endif
bytes_written += file->WriteString(message); bytes_written += file->WriteString(message);
// Option to log each line rather than 4k buffers // 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(); file->Flush();
}
using namespace Common::Literals; using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed. // 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 auto write_limit = Settings::values.extended_logging.GetValue() ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit; const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) { if (entry.log_level >= Level::Error || write_limit_exceeded) {
if (write_limit_exceeded) { // Stop writing after the write limit is exceeded.
// Stop writing after the write limit is exceeded. // Don't close the file so we can print a stacktrace if necessary
// Don't close the file so we can print a stacktrace if necessary if (write_limit_exceeded)
enabled = false; enabled = false;
}
file->Flush(); file->Flush();
} }
} }
@ -157,8 +151,8 @@ public:
private: private:
std::unique_ptr<FS::IOFile> file; std::unique_ptr<FS::IOFile> file;
bool enabled = true;
std::size_t bytes_written = 0; std::size_t bytes_written = 0;
bool enabled = true;
}; };
/** /**
@ -209,9 +203,8 @@ bool initialization_in_progress_suppress_logging = true;
class Impl { class Impl {
public: public:
static Impl& Instance() { static Impl& Instance() {
if (!instance) { if (!instance)
throw std::runtime_error("Using Logging instance before its initialization"); throw std::runtime_error("Using Logging instance before its initialization");
}
return *instance; return *instance;
} }
@ -277,25 +270,21 @@ private:
}; };
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
message_queue.PopWait(entry, stop_token); message_queue.PopWait(entry, stop_token);
if (entry.filename != nullptr) { if (entry.filename != nullptr)
write_logs(); write_logs();
}
} }
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a // 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. // case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; 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(); write_logs();
}
}); });
} }
void StopBackendThread() { void StopBackendThread() {
backend_thread.request_stop(); backend_thread.request_stop();
if (backend_thread.joinable()) { if (backend_thread.joinable())
backend_thread.join(); backend_thread.join();
}
ForEachBackend([](Backend& backend) { backend.Flush(); }); ForEachBackend([](Backend& backend) { backend.Flush(); });
} }
@ -304,7 +293,6 @@ private:
using std::chrono::duration_cast; using std::chrono::duration_cast;
using std::chrono::microseconds; using std::chrono::microseconds;
using std::chrono::steady_clock; using std::chrono::steady_clock;
return { return {
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin), .timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
.log_class = log_class, .log_class = log_class,

View file

@ -3,12 +3,12 @@
#include <array> #include <array>
#include <cstdio> #include <cstdio>
#include <cstdint>
#include <string>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#endif #elif defined(__ANDROID__)
#ifdef ANDROID
#include <android/log.h> #include <android/log.h>
#endif #endif
@ -21,123 +21,76 @@
namespace Common::Log { namespace Common::Log {
std::string FormatLogMessage(const Entry& entry) { std::string FormatLogMessage(const Entry& entry) {
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000); auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000);
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000); auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
char const* class_name = GetLogClassName(entry.log_class);
const char* class_name = GetLogClassName(entry.log_class); char const* level_name = GetLevelName(entry.log_level);
const char* level_name = GetLevelName(entry.log_level);
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional, return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
class_name, level_name, entry.filename, entry.function, entry.line_num, class_name, level_name, entry.filename, entry.function, entry.line_num,
entry.message); entry.message);
} }
void PrintMessage(const Entry& entry) { void PrintMessage(const Entry& entry) {
const auto str = FormatLogMessage(entry).append(1, '\n'); #ifdef _WIN32
fputs(str.c_str(), stderr); auto const str = FormatLogMessage(entry).append(1, '\n');
#else
#define ESC "\x1b"
auto const str = std::string{[&]() {
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
case Level::Trace:
case Level::Count: return ESC "[1;30m"; // Grey
}
}()}.append(FormatLogMessage(entry)).append(ESC "[0m\n");
#undef ESC
#endif
fwrite(str.c_str(), 1, str.size(), stderr);
} }
void PrintColoredMessage(const Entry& entry) { void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32 #ifdef _WIN32
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE); HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
if (console_handle == INVALID_HANDLE_VALUE) { if (console_handle == INVALID_HANDLE_VALUE)
return; return;
}
CONSOLE_SCREEN_BUFFER_INFO original_info = {}; CONSOLE_SCREEN_BUFFER_INFO original_info = {};
GetConsoleScreenBufferInfo(console_handle, &original_info); GetConsoleScreenBufferInfo(console_handle, &original_info);
WORD color = [&]() {
WORD color = 0; switch (entry.log_level) {
switch (entry.log_level) { case Level::Debug: return FOREGROUND_GREEN | FOREGROUND_BLUE; // Cyan
case Level::Trace: // Grey case Level::Info: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // Bright gray
color = FOREGROUND_INTENSITY; case Level::Warning: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break; case Level::Error: return FOREGROUND_RED | FOREGROUND_INTENSITY;
case Level::Debug: // Cyan case Level::Critical: return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
color = FOREGROUND_GREEN | FOREGROUND_BLUE; case Level::Trace:
break; case Level::Count: return FOREGROUND_INTENSITY; // Grey
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();
}
SetConsoleTextAttribute(console_handle, color); 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 #endif
PrintMessage(entry); PrintMessage(entry);
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(console_handle, original_info.wAttributes); SetConsoleTextAttribute(console_handle, original_info.wAttributes);
#else
fputs(ESC "[0m", stderr);
#undef ESC
#endif #endif
} }
void PrintMessageToLogcat(const Entry& entry) { void PrintMessageToLogcat(const Entry& entry) {
#ifdef ANDROID #ifdef ANDROID
const auto str = FormatLogMessage(entry); android_LogPriority android_log_priority = [&]() {
switch (entry.log_level) {
android_LogPriority android_log_priority; case Level::Debug: return ANDROID_LOG_DEBUG;
switch (entry.log_level) { case Level::Info: return ANDROID_LOG_INFO;
case Level::Trace: case Level::Warning: return ANDROID_LOG_WARN;
android_log_priority = ANDROID_LOG_VERBOSE; case Level::Error: return ANDROID_LOG_ERROR;
break; case Level::Critical: return ANDROID_LOG_FATAL;
case Level::Debug: case Level::Count:
android_log_priority = ANDROID_LOG_DEBUG; case Level::Trace: return ANDROID_LOG_VERBOSE;
break; }
case Level::Info: }();
android_log_priority = ANDROID_LOG_INFO; auto const str = FormatLogMessage(entry);
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_log_print(android_log_priority, "YuzuNative", "%s", str.c_str()); __android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str());
#endif #endif
} }

View file

@ -28,26 +28,18 @@ class DebuggerBackend {
public: public:
virtual ~DebuggerBackend() = default; virtual ~DebuggerBackend() = default;
/** /// Can be invoked from a callback to synchronously wait for more data.
* 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.
* Will return as soon as least one byte is received. Reads up to 4096 bytes.
*/
virtual std::span<const u8> ReadFromClient() = 0; virtual std::span<const u8> ReadFromClient() = 0;
/** /// Can be invoked from a callback to write data to the client.
* Can be invoked from a callback to write data to the client. /// Returns immediately after the data is sent.
* Returns immediately after the data is sent.
*/
virtual void WriteToClient(std::span<const u8> data) = 0; 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; 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; virtual void SetActiveThread(Kernel::KThread* thread) = 0;
}; };
@ -57,30 +49,20 @@ public:
virtual ~DebuggerFrontend() = default; 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; virtual void Connected() = 0;
/** /// Called when emulation has stopped.
* Called when emulation has stopped.
*/
virtual void Stopped(Kernel::KThread* thread) = 0; virtual void Stopped(Kernel::KThread* thread) = 0;
/** /// Called when emulation is shutting down.
* Called when emulation is shutting down.
*/
virtual void ShuttingDown() = 0; 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; virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
/** /// Called when new data is asynchronously received on the client socket.
* Called when new data is asynchronously received on the client socket. /// A list of actions to perform is returned.
* A list of actions to perform is returned.
*/
[[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0; [[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
protected: protected:

View file

@ -49,28 +49,14 @@ static u8 CalculateChecksum(std::string_view data) {
static std::string EscapeGDB(std::string_view data) { static std::string EscapeGDB(std::string_view data) {
std::string escaped; std::string escaped;
escaped.reserve(data.size()); for (char const c : data)
for (char c : data) {
switch (c) { switch (c) {
case '#': case '#': escaped += "}\x03"; break;
escaped += "}\x03"; case '$': escaped += "}\x04"; break;
break; case '*': escaped += "}\x0a"; break;
case '$': case '}': escaped += "}\x5d"; break;
escaped += "}\x04"; default: escaped += c; break;
break;
case '*':
escaped += "}\x0a";
break;
case '}':
escaped += "}\x5d";
break;
default:
escaped += c;
break;
} }
}
return escaped; return escaped;
} }
@ -82,38 +68,26 @@ static std::string EscapeXML(std::string_view data) {
} }
std::string escaped; std::string escaped;
escaped.reserve(data.size()); for (char32_t const c : converted)
for (char32_t c : converted) {
switch (c) { switch (c) {
case '&': case '&': escaped += "&amp;"; break;
escaped += "&amp;"; case '"': escaped += "&quot;"; break;
break; case '<': escaped += "&lt;"; break;
case '"': case '>': escaped += "&gt;"; break;
escaped += "&quot;";
break;
case '<':
escaped += "&lt;";
break;
case '>':
escaped += "&gt;";
break;
default: default:
if (c > 0x7f) { if (c > 0x7f) {
escaped += fmt::format("&#{};", static_cast<u32>(c)); escaped += fmt::format("&#{};", u32(c));
} else { } else {
escaped += static_cast<char>(c); escaped += char(c);
} }
break; break;
} }
}
return escaped; return escaped;
} }
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_) GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} { : DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
if (GetProcess()->Is64Bit()) { if (debug_process->Is64Bit()) {
arch = std::make_unique<GDBStubA64>(); arch = std::make_unique<GDBStubA64>();
} else { } else {
arch = std::make_unique<GDBStubA32>(); 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> GDBStub::ClientData(std::span<const u8> data) {
std::vector<DebuggerAction> actions;
current_command.insert(current_command.end(), data.begin(), data.end()); current_command.insert(current_command.end(), data.begin(), data.end());
std::vector<DebuggerAction> actions;
while (current_command.size() != 0) { while (!current_command.empty())
ProcessData(actions); ProcessData(actions);
}
return actions; return actions;
} }
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) { void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
const char c{current_command[0]}; const char c = current_command[0];
// Acknowledgement // Acknowledgement
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) { if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
current_command.erase(current_command.begin()); current_command.erase(current_command.begin());
return;
}
// Interrupt // Interrupt
if (c == GDB_STUB_INT3) { } else if (c == GDB_STUB_INT3) {
LOG_INFO(Debug_GDBStub, "Received interrupt"); LOG_INFO(Debug_GDBStub, "Received interrupt");
current_command.erase(current_command.begin()); current_command.erase(current_command.begin());
actions.push_back(DebuggerAction::Interrupt); actions.push_back(DebuggerAction::Interrupt);
SendStatus(GDB_STUB_ACK); SendStatus(GDB_STUB_ACK);
return;
}
// Otherwise, require the data to be the start of a command // 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()); LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
current_command.clear(); current_command.clear();
SendStatus(GDB_STUB_NACK); 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 { } 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) { if (packet.length() == 0) {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
return; return;
} } else if (packet.starts_with("vCont")) {
if (packet.starts_with("vCont")) {
HandleVCont(packet.substr(5), actions); HandleVCont(packet.substr(5), actions);
return; return;
} }
@ -218,14 +176,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
switch (packet[0]) { switch (packet[0]) {
case 'H': { case 'H': {
Kernel::KThread* thread{nullptr}; s64 thread_id = strtoll(command.data() + 1, nullptr, 16);
s64 thread_id{strtoll(command.data() + 1, nullptr, 16)}; Kernel::KThread* thread = thread_id >= 1 ? GetThreadByID(thread_id) : backend.GetActiveThread();
if (thread_id >= 1) {
thread = GetThreadByID(thread_id);
} else {
thread = backend.GetActiveThread();
}
if (thread) { if (thread) {
SendReply(GDB_STUB_REPLY_OK); SendReply(GDB_STUB_REPLY_OK);
backend.SetActiveThread(thread); backend.SetActiveThread(thread);
@ -235,7 +187,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
break; break;
} }
case 'T': { case 'T': {
s64 thread_id{strtoll(command.data(), nullptr, 16)}; s64 thread_id = strtoll(command.data(), nullptr, 16);
if (GetThreadByID(thread_id)) { if (GetThreadByID(thread_id)) {
SendReply(GDB_STUB_REPLY_OK); SendReply(GDB_STUB_REPLY_OK);
} else { } else {
@ -262,27 +214,26 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
SendReply(GDB_STUB_REPLY_OK); SendReply(GDB_STUB_REPLY_OK);
break; break;
case 'p': { 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)); SendReply(arch->RegRead(backend.GetActiveThread(), reg));
break; break;
} }
case 'P': { case 'P': {
const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1}; 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 size_t reg = size_t(strtoll(command.data(), nullptr, 16));
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep)); arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
SendReply(GDB_STUB_REPLY_OK); SendReply(GDB_STUB_REPLY_OK);
break; break;
} }
case 'm': { case 'm': {
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; 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 addr = size_t(strtoll(command.data(), nullptr, 16));
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))}; const size_t size = size_t(strtoll(command.data() + sep, nullptr, 16));
std::vector<u8> mem(size); 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. // Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions.lower_bound(addr); for (auto it = replaced_instructions.lower_bound(addr); it != replaced_instructions.end() && it->first < addr + size; it++) {
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
// Get the bytes of the instruction we previously replaced. // Get the bytes of the instruction we previously replaced.
const u32 original_bytes = it->second; 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 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 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 addr{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 size{size_t(strtoll(command.data() + size_sep, nullptr, 16))};
const auto mem_substr{std::string_view(command).substr(mem_sep)}; const auto mem_substr{std::string_view(command).substr(mem_sep)};
const auto mem{Common::HexStringToVector(mem_substr, false)}; const auto mem{Common::HexStringToVector(mem_substr, false)};
if (GetMemory().WriteBlock(addr, mem.data(), size)) { if (debug_process->GetMemory().WriteBlock(addr, mem.data(), size)) {
Core::InvalidateInstructionCacheRange(GetProcess(), addr, size); Core::InvalidateInstructionCacheRange(debug_process, addr, size);
SendReply(GDB_STUB_REPLY_OK); SendReply(GDB_STUB_REPLY_OK);
} else { } else {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
@ -349,14 +300,13 @@ enum class BreakpointType {
}; };
void GDBStub::HandleBreakpointInsert(std::string_view command) { void GDBStub::HandleBreakpointInsert(std::string_view command) {
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; const auto type = BreakpointType(strtoll(command.data(), nullptr, 16));
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; 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(), ',') - const auto size_sep = std::find(command.begin() + addr_sep, command.end(), ',') - command.begin() + 1;
command.begin() + 1}; const size_t addr = size_t(strtoll(command.data() + addr_sep, nullptr, 16));
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t size = size_t(strtoll(command.data() + size_sep, nullptr, 16));
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
return; return;
} }
@ -365,20 +315,19 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
switch (type) { switch (type) {
case BreakpointType::Software: case BreakpointType::Software:
replaced_instructions[addr] = GetMemory().Read32(addr); replaced_instructions[addr] = debug_process->GetMemory().Read32(addr);
GetMemory().Write32(addr, arch->BreakpointInstruction()); debug_process->GetMemory().Write32(addr, arch->BreakpointInstruction());
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32)); Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32));
success = true; success = true;
break; break;
case BreakpointType::WriteWatch: case BreakpointType::WriteWatch:
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break; break;
case BreakpointType::ReadWatch: case BreakpointType::ReadWatch:
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break; break;
case BreakpointType::AccessWatch: case BreakpointType::AccessWatch:
success = success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break; break;
case BreakpointType::Hardware: case BreakpointType::Hardware:
default: default:
@ -393,41 +342,37 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
} }
} }
void GDBStub::HandleBreakpointRemove(std::string_view command) { void GDBStub::HandleBreakpointRemove(std::string_view sv) {
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))}; const auto type = BreakpointType(strtoll(sv.data(), nullptr, 16));
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1}; const auto addr_sep = std::find(sv.begin(), sv.end(), ',') - sv.begin() + 1;
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') - const auto size_sep = std::find(sv.begin() + addr_sep, sv.end(), ',') - sv.begin() + 1;
command.begin() + 1}; const size_t addr = size_t(strtoll(sv.data() + addr_sep, nullptr, 16));
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))}; const size_t size = size_t(strtoll(sv.data() + size_sep, nullptr, 16));
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) { if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR); SendReply(GDB_STUB_REPLY_ERR);
return; return;
} }
bool success{}; bool success = false;
switch (type) { switch (type) {
case BreakpointType::Software: { case BreakpointType::Software: {
const auto orig_insn{replaced_instructions.find(addr)}; if (auto const orig_insn = replaced_instructions.find(addr); orig_insn != replaced_instructions.end()) {
if (orig_insn != replaced_instructions.end()) { debug_process->GetMemory().Write32(addr, orig_insn->second);
GetMemory().Write32(addr, orig_insn->second); Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32));
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
replaced_instructions.erase(addr); replaced_instructions.erase(addr);
success = true; success = true;
} }
break; break;
} }
case BreakpointType::WriteWatch: case BreakpointType::WriteWatch:
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write); success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break; break;
case BreakpointType::ReadWatch: case BreakpointType::ReadWatch:
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read); success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break; break;
case BreakpointType::AccessWatch: case BreakpointType::AccessWatch:
success = success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break; break;
case BreakpointType::Hardware: case BreakpointType::Hardware:
default: default:
@ -454,22 +399,21 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
} }
} }
void GDBStub::HandleQuery(std::string_view command) { void GDBStub::HandleQuery(std::string_view sv) {
if (command.starts_with("TStatus")) { if (sv.starts_with("TStatus")) {
// no tracepoint support // no tracepoint support
SendReply("T0"); 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+;" SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
"vContSupported+;QStartNoAckMode+"); "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()}; const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30))); SendReply(PaginateBuffer(target_xml, sv.substr(30)));
} else if (command.starts_with("Offsets")) { } else if (sv.starts_with("Offsets")) {
const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess()); const auto main_offset = Core::FindMainModuleEntrypoint(debug_process);
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset))); SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
} else if (command.starts_with("Xfer:libraries:read::")) { } else if (sv.starts_with("Xfer:libraries:read::")) {
auto modules = Core::FindModules(GetProcess()); auto modules = Core::FindModules(debug_process);
std::string buffer; std::string buffer;
buffer += R"(<?xml version="1.0"?>)"; buffer += R"(<?xml version="1.0"?>)";
buffer += "<library-list>"; buffer += "<library-list>";
@ -479,24 +423,23 @@ void GDBStub::HandleQuery(std::string_view command) {
} }
buffer += "</library-list>"; buffer += "</library-list>";
SendReply(PaginateBuffer(buffer, command.substr(21))); SendReply(PaginateBuffer(buffer, sv.substr(21)));
} else if (command.starts_with("fThreadInfo")) { } else if (sv.starts_with("fThreadInfo")) {
// beginning of list // beginning of list
const auto& threads = GetProcess()->GetThreadList(); const auto& threads = debug_process->GetThreadList();
std::vector<std::string> thread_ids; 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())); thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
}
SendReply(fmt::format("m{}", fmt::join(thread_ids, ","))); SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
} else if (command.starts_with("sThreadInfo")) { } else if (sv.starts_with("sThreadInfo")) {
// end of list // end of list
SendReply("l"); SendReply("l");
} else if (command.starts_with("Xfer:threads:read::")) { } else if (sv.starts_with("Xfer:threads:read::")) {
std::string buffer; std::string buffer;
buffer += R"(<?xml version="1.0"?>)"; buffer += R"(<?xml version="1.0"?>)";
buffer += "<threads>"; buffer += "<threads>";
const auto& threads = GetProcess()->GetThreadList(); const auto& threads = debug_process->GetThreadList();
for (const auto& thread : threads) { for (const auto& thread : threads) {
auto thread_name{Core::GetThreadName(&thread)}; auto thread_name{Core::GetThreadName(&thread)};
if (!thread_name) { if (!thread_name) {
@ -510,109 +453,95 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += "</threads>"; buffer += "</threads>";
SendReply(PaginateBuffer(buffer, command.substr(19))); SendReply(PaginateBuffer(buffer, sv.substr(19)));
} else if (command.starts_with("Attached")) { } else if (sv.starts_with("Attached")) {
SendReply("0"); SendReply("0");
} else if (command.starts_with("StartNoAckMode")) { } else if (sv.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,")) { } else if (sv.starts_with("Rcmd,")) {
HandleRcmd(Common::HexStringToVector(command.substr(5), false)); HandleRcmd(Common::HexStringToVector(sv.substr(5), false));
} else { } else {
SendReply(GDB_STUB_REPLY_EMPTY); SendReply(GDB_STUB_REPLY_EMPTY);
} }
} }
void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) { void GDBStub::HandleVCont(std::string_view sv, std::vector<DebuggerAction>& actions) {
if (command == "?") { // Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
// Continuing and stepping are supported if (sv == "?") {
// (signal is ignored, but required for GDB to use vCont)
SendReply("vCont;c;C;s;S"); 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));
}
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
: DebuggerAction::StepThreadUnlocked);
} else { } else {
actions.push_back(DebuggerAction::Continue); 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);
}
} }
} }
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ #define MEMORY_STATE_LIST \
{"----- Free ------", Kernel::Svc::MemoryState::Free}, MEMORY_STATE_ELEM(Free) \
{"Io ", Kernel::Svc::MemoryState::Io}, MEMORY_STATE_ELEM(Io) \
{"Static ", Kernel::Svc::MemoryState::Static}, MEMORY_STATE_ELEM(Static) \
{"Code ", Kernel::Svc::MemoryState::Code}, MEMORY_STATE_ELEM(Code) \
{"CodeData ", Kernel::Svc::MemoryState::CodeData}, MEMORY_STATE_ELEM(CodeData) \
{"Normal ", Kernel::Svc::MemoryState::Normal}, MEMORY_STATE_ELEM(Normal) \
{"Shared ", Kernel::Svc::MemoryState::Shared}, MEMORY_STATE_ELEM(Shared) \
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, MEMORY_STATE_ELEM(AliasCode) \
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, MEMORY_STATE_ELEM(AliasCodeData) \
{"Ipc ", Kernel::Svc::MemoryState::Ipc}, MEMORY_STATE_ELEM(Ipc) \
{"Stack ", Kernel::Svc::MemoryState::Stack}, MEMORY_STATE_ELEM(Stack) \
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, MEMORY_STATE_ELEM(ThreadLocal) \
{"Transferred ", Kernel::Svc::MemoryState::Transferred}, MEMORY_STATE_ELEM(Transferred) \
{"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred}, MEMORY_STATE_ELEM(SharedTransferred) \
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, MEMORY_STATE_ELEM(SharedCode) \
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, MEMORY_STATE_ELEM(Inaccessible) \
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, MEMORY_STATE_ELEM(NonSecureIpc) \
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, MEMORY_STATE_ELEM(NonDeviceIpc) \
{"Kernel ", Kernel::Svc::MemoryState::Kernel}, MEMORY_STATE_ELEM(Kernel) \
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, MEMORY_STATE_ELEM(GeneratedCode) \
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, MEMORY_STATE_ELEM(CodeOut) \
{"Coverage ", Kernel::Svc::MemoryState::Coverage}, MEMORY_STATE_ELEM(Coverage)
}}; switch (state) {
for (size_t i = 0; i < MemoryStateNames.size(); i++) { #define MEMORY_STATE_ELEM(elem) case Kernel::Svc::MemoryState::elem: return #elem;
if (std::get<1>(MemoryStateNames[i]) == state) { MEMORY_STATE_LIST
return std::get<0>(MemoryStateNames[i]); #undef MEMORY_STATE_LIST
} default: return "Unknown";
} }
return "Unknown ";
} }
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
if (info.state == Kernel::Svc::MemoryState::Free) { if (info.state == Kernel::Svc::MemoryState::Free) {
return " "; return " ";
} } else {
switch (info.permission) {
switch (info.permission) { case Kernel::Svc::MemoryPermission::ReadExecute: return "r-x";
case Kernel::Svc::MemoryPermission::ReadExecute: case Kernel::Svc::MemoryPermission::Read: return "r--";
return "r-x"; case Kernel::Svc::MemoryPermission::ReadWrite: return "rw-";
case Kernel::Svc::MemoryPermission::Read: default: return "---";
return "r--"; }
case Kernel::Svc::MemoryPermission::ReadWrite:
return "rw-";
default:
return "---";
} }
} }
void GDBStub::HandleRcmd(const std::vector<u8>& command) { void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply; std::string reply;
auto& page_table = debug_process->GetPageTable();
auto* process = GetProcess();
auto& page_table = process->GetPageTable();
if (command_str == "fastmem" || command_str == "get fastmem") { if (command_str == "fastmem" || command_str == "get fastmem") {
if (Settings::IsFastmemEnabled()) { if (Settings::IsFastmemEnabled()) {
const auto& impl = page_table.GetImpl(); const auto& impl = page_table.GetImpl();
@ -627,11 +556,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = "Fastmem is not enabled.\n"; reply = "Fastmem is not enabled.\n";
} }
} else if (command_str == "info" || command_str == "get info") { } 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" reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n", "Program Id: {:#018x}\n",
process->GetProcessId(), process->GetName(), process->GetProgramId()); debug_process->GetProcessId(),
debug_process->GetName(),
debug_process->GetProgramId());
reply += fmt::format( reply += fmt::format(
"Layout:\n" "Layout:\n"
" Alias: {:#012x} - {:#012x}\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()),
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1); GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
for (const auto& [vaddr, name] : modules) { for (const auto& [vaddr, name] : modules)
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, GetInteger(Core::GetModuleEnd(debug_process, vaddr)), name);
GetInteger(Core::GetModuleEnd(process, vaddr)), name);
}
} else if (command_str == "mappings" || command_str == "get mappings") { } else if (command_str == "mappings" || command_str == "get mappings") {
reply = "Mappings:\n"; reply = "Mappings:\n";
VAddr cur_addr = 0; 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; 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; break;
}
cur_addr = next_address; cur_addr = next_address;
} }
} else { } else {
@ -698,20 +625,16 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
} }
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
auto& threads{GetProcess()->GetThreadList()}; auto& threads = debug_process->GetThreadList();
for (auto& thread : threads) { for (auto& thread : threads)
if (thread.GetThreadId() == thread_id) { if (thread.GetThreadId() == thread_id)
return std::addressof(thread); return std::addressof(thread);
}
}
return nullptr; return nullptr;
} }
std::vector<char>::const_iterator GDBStub::CommandEnd() const { std::vector<char>::const_iterator GDBStub::CommandEnd() const {
// Find the end marker // 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 // Require the checksum to be present
return (std::min)(end + 2, current_command.end()); return (std::min)(end + 2, current_command.end());
} }
@ -737,8 +660,7 @@ std::optional<std::string> GDBStub::DetachCommand() {
// Verify checksum // Verify checksum
if (calculated != received) { if (calculated != received) {
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", calculated, received);
calculated, received);
return std::nullopt; return std::nullopt;
} }
@ -746,9 +668,8 @@ std::optional<std::string> GDBStub::DetachCommand() {
} }
void GDBStub::SendReply(std::string_view data) { void GDBStub::SendReply(std::string_view data) {
const auto escaped{EscapeGDB(data)}; const auto escaped = EscapeGDB(data);
const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, const auto output = fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, CalculateChecksum(escaped));
CalculateChecksum(escaped))};
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
// C++ string support is complete rubbish // C++ string support is complete rubbish
@ -758,21 +679,11 @@ void GDBStub::SendReply(std::string_view data) {
} }
void GDBStub::SendStatus(char status) { void GDBStub::SendStatus(char status) {
if (no_ack) { if (!no_ack) {
return; 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 } // namespace Core

View file

@ -24,19 +24,14 @@ namespace Core {
class System; class System;
class GDBStub : public DebuggerFrontend { struct GDBStub : public DebuggerFrontend {
public: explicit GDBStub(DebuggerBackend& backend, Core::System& system, Kernel::KProcess* debug_process);
explicit GDBStub(DebuggerBackend& backend, Core::System& system,
Kernel::KProcess* debug_process);
~GDBStub() override; ~GDBStub() override;
void Connected() override; void Connected() override;
void Stopped(Kernel::KThread* thread) override; void Stopped(Kernel::KThread* thread) override;
void ShuttingDown() override; void ShuttingDown() override;
void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override; void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override; std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
private:
void ProcessData(std::vector<DebuggerAction>& actions); void ProcessData(std::vector<DebuggerAction>& actions);
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);
@ -47,14 +42,8 @@ private:
std::vector<char>::const_iterator CommandEnd() const; std::vector<char>::const_iterator CommandEnd() const;
std::optional<std::string> DetachCommand(); std::optional<std::string> DetachCommand();
Kernel::KThread* GetThreadByID(u64 thread_id); Kernel::KThread* GetThreadByID(u64 thread_id);
void SendReply(std::string_view data); void SendReply(std::string_view data);
void SendStatus(char status); void SendStatus(char status);
Kernel::KProcess* GetProcess();
Core::Memory::Memory& GetMemory();
private:
Core::System& system; Core::System& system;
Kernel::KProcess* debug_process; Kernel::KProcess* debug_process;
std::unique_ptr<GDBStubArch> arch; std::unique_ptr<GDBStubArch> arch;