[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:
parent
e33d426ac4
commit
87c0994a0d
5 changed files with 257 additions and 434 deletions
|
|
@ -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,14 +62,12 @@ 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 {
|
||||
// stderr shouldn't be buffered
|
||||
|
|
@ -102,46 +97,45 @@ public:
|
|||
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");
|
||||
}
|
||||
// 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
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
#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 +21,76 @@
|
|||
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 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;
|
||||
WORD color = [&]() {
|
||||
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();
|
||||
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;
|
||||
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::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:
|
||||
UNREACHABLE();
|
||||
case Level::Trace: return ANDROID_LOG_VERBOSE;
|
||||
}
|
||||
}();
|
||||
auto const str = FormatLogMessage(entry);
|
||||
__android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str());
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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 +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<DebuggerAction> ClientData(std::span<const u8> data) = 0;
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -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<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,58 +122,44 @@ 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;
|
||||
}
|
||||
|
||||
} else {
|
||||
// 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) {
|
||||
if (auto const cmd = DetachCommand(); cmd) {
|
||||
SendStatus(GDB_STUB_ACK);
|
||||
ExecuteCommand(*command, actions);
|
||||
ExecuteCommand(*cmd, actions);
|
||||
} else {
|
||||
SendStatus(GDB_STUB_NACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
|
||||
LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
|
||||
|
|
@ -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))};
|
||||
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<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};
|
||||
|
||||
} else {
|
||||
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) {
|
||||
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"))) {
|
||||
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"))) {
|
||||
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);
|
||||
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 " ";
|
||||
}
|
||||
|
||||
} 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 "---";
|
||||
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;
|
||||
}
|
||||
|
||||
std::array<u8, 1> buf = {static_cast<u8>(status)};
|
||||
if (!no_ack) {
|
||||
std::array<u8, 1> buf = {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
|
||||
|
|
|
|||
|
|
@ -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<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 +42,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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue