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(FS::RemoveFile(old_filename)); static_cast(FS::RenameFile(filename, old_filename)); - file = std::make_unique(filename, FS::FileAccessMode::Write, - FS::FileType::TextFile); + file = std::make_unique(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 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(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..d9922d74e0 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp @@ -3,12 +3,12 @@ #include #include +#include +#include #ifdef _WIN32 #include -#endif - -#ifdef ANDROID +#elif defined(__ANDROID__) #include #endif @@ -21,123 +21,76 @@ namespace Common::Log { std::string FormatLogMessage(const Entry& entry) { - unsigned int time_seconds = static_cast(entry.timestamp.count() / 1000000); - unsigned int time_fractional = static_cast(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 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) { #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 = [&]() { + 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; + case Level::Trace: + case Level::Count: 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..622473de21 100644 --- a/src/core/debugger/debugger_interface.h +++ b/src/core/debugger/debugger_interface.h @@ -28,26 +28,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 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 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 +49,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 ClientData(std::span 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 += "&"; - break; - case '"': - escaped += """; - break; - case '<': - escaped += "<"; - break; - case '>': - escaped += ">"; - break; + case '&': escaped += "&"; break; + case '"': escaped += """; break; + case '<': escaped += "<"; break; + case '>': escaped += ">"; break; default: if (c > 0x7f) { - escaped += fmt::format("&#{};", static_cast(c)); + escaped += fmt::format("&#{};", u32(c)); } else { - escaped += static_cast(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(); } else { arch = std::make_unique(); @@ -148,56 +122,42 @@ void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& } std::vector GDBStub::ClientData(std::span data) { - std::vector actions; current_command.insert(current_command.end(), data.begin(), data.end()); - - while (current_command.size() != 0) { + std::vector actions; + while (!current_command.empty()) ProcessData(actions); - } - return actions; } void GDBStub::ProcessData(std::vector& 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= 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(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(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(strtoll(command.data(), nullptr, 16))}; - const size_t size{static_cast(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 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(strtoll(command.data(), nullptr, 16))}; - const size_t size{static_cast(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(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(strtoll(command.data() + addr_sep, nullptr, 16))}; - const size_t size{static_cast(strtoll(command.data() + size_sep, 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 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 (!GetMemory().IsValidVirtualAddressRange(addr, size)) { + 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(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(strtoll(command.data() + addr_sep, nullptr, 16))}; - const size_t size{static_cast(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"()"; buffer += ""; @@ -479,24 +423,23 @@ void GDBStub::HandleQuery(std::string_view command) { } buffer += ""; - 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 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"()"; buffer += ""; - 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 += ""; - 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& 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& 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 entries; - boost::split(entries, command.substr(1), boost::is_any_of(";")); - for (const auto& thread_action : entries) { - std::vector 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); + Kernel::KThread* stepped_thread = nullptr; + bool lock_execution = true; + std::vector entries; + boost::split(entries, sv.substr(1), boost::is_any_of(";")); + for (auto const& thread_action : entries) { + std::vector 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) { - constexpr std::array, 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& command) { std::string_view command_str{reinterpret_cast(&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& 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& 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& 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& 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::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 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 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 buf = {u8(status)}; + LOG_TRACE(Debug_GDBStub, "Writing status: {}", status); + backend.WriteToClient(buf); } - - std::array buf = {static_cast(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..6e6e1b1bc3 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -24,19 +24,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 ClientData(std::span data) override; - -private: void ProcessData(std::vector& actions); void ExecuteCommand(std::string_view packet, std::vector& actions); void HandleVCont(std::string_view command, std::vector& actions); @@ -47,14 +42,8 @@ private: std::vector::const_iterator CommandEnd() const; std::optional 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 arch;