From c7b0d779702f23d44e99c7e99ee9470734924a2b Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 9 Sep 2026 13:54:51 +0400 Subject: [PATCH] loader: port guest fault diagnostics and remove host stack traces Co-authored-by: Ali Almohaya --- src/common/assert.cpp | 20 +------- src/common/debug.cpp | 13 ----- src/common/debug.h | 27 +--------- src/common/platform/sysDbg.h | 4 +- src/common/platform/sysLinuxDbg.cpp | 51 ------------------- src/common/platform/sysWindowsDbg.cpp | 12 ----- src/common/threads.cpp | 1 - src/graphics/host_gpu/pageManager.cpp | 19 ------- .../recompiler/backend/spirv/SpirvBuilder.cpp | 2 +- src/loader/runtimeLinker.cpp | 36 +++++++++++++ src/main.cpp | 1 - 11 files changed, 41 insertions(+), 145 deletions(-) diff --git a/src/common/assert.cpp b/src/common/assert.cpp index 5a4d9c987..22cb3f27d 100644 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -1,6 +1,5 @@ #include "common/assert.h" -#include "common/debug.h" #include "common/logging/log.h" #include "common/subsystems.h" #include "kytyGitVersion.h" @@ -12,25 +11,10 @@ namespace Common { -#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \ - KYTY_COMPILER == KYTY_COMPILER_CLANG -constexpr int PRINT_STACK_FROM = 4; -#else -constexpr int PRINT_STACK_FROM = 2; -#endif - static std::string BuildFatalReport(const char* title, std::string_view text, const char* file, int line) { - DebugStack stack; - DebugStack::Trace(&stack); - - std::string report = "--- Build ---\n" KYTY_BUILD_LABEL "\n--- Stack Trace ---\n"; - for (int i = PRINT_STACK_FROM; i < stack.depth; i++) { - report += fmt::format("[{}] {:016x}\n", i - PRINT_STACK_FROM, - static_cast(stack.GetAddr(i))); - } - report += fmt::format("{}\n{} in {}:{}\n", title, text, file, line); - return report; + return fmt::format("--- Build ---\n{}\n{}\n{} in {}:{}\n", KYTY_BUILD_LABEL, title, text, file, + line); } static int DbgReport(const char* title, std::string_view text, const char* file, int line) { diff --git a/src/common/debug.cpp b/src/common/debug.cpp index fcdf27a33..c24a74785 100644 --- a/src/common/debug.cpp +++ b/src/common/debug.cpp @@ -1,20 +1,7 @@ #include "common/debug.h" -#include "common/platform/sysDbg.h" - namespace Common { -void DebugStack::Print(int from, [[maybe_unused]] bool with_name) const { - for (int i = from; i < depth; i++) { - printf("[%d] %016" PRIx64 "\n", i - from, static_cast(GetAddr(i))); - } -} - -void DebugStack::Trace(DebugStack* stack) { - stack->depth = DEBUG_MAX_STACK_DEPTH; - SysStackWalk(stack->stack.data(), &stack->depth); -} - std::string Debug::GetCompiler() { #if KYTY_COMPILER == KYTY_COMPILER_CLANG return "clang"; diff --git a/src/common/debug.h b/src/common/debug.h index a0c91c82c..a82bfac86 100644 --- a/src/common/debug.h +++ b/src/common/debug.h @@ -2,42 +2,17 @@ #define KYTY_COMMON_DEBUG_H_ #include "common/common.h" -#include "common/stringUtils.h" -#include -#include +#include namespace Common { -#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && \ - KYTY_COMPILER == KYTY_COMPILER_CLANG -constexpr int DEBUG_MAX_STACK_DEPTH = 20; -#else -constexpr int DEBUG_MAX_STACK_DEPTH = 15; -#endif - namespace Debug { std::string GetCompiler(); std::string GetLinker(); std::string GetBitness(); } // namespace Debug -struct DebugStack { - int depth {0}; - std::array stack {}; - - [[nodiscard]] uintptr_t GetAddr(int i) const { return reinterpret_cast(stack[i]); } - - void Print(int from, bool with_name = true) const; - - void CopyTo(DebugStack* s) const { - std::memcpy(s->stack.data(), stack.data(), sizeof(void*) * depth); - s->depth = depth; - } - - static void Trace(DebugStack* stack); -}; - } // namespace Common #endif /* KYTY_COMMON_DEBUG_H_ */ diff --git a/src/common/platform/sysDbg.h b/src/common/platform/sysDbg.h index d9397af1a..043fa1b1f 100644 --- a/src/common/platform/sysDbg.h +++ b/src/common/platform/sysDbg.h @@ -30,8 +30,6 @@ struct sys_dbg_stack_info_t { #endif }; -void SysStackWalk(void** stack, int* depth); -void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references) -void SysStackUsagePrint(sys_dbg_stack_info_t& stack); // NOLINT(google-runtime-references) +void SysStackUsage(sys_dbg_stack_info_t& s); // NOLINT(google-runtime-references) #endif /* KYTY_COMMON_PLATFORM_SYSDBG_H_ */ diff --git a/src/common/platform/sysLinuxDbg.cpp b/src/common/platform/sysLinuxDbg.cpp index c6701e9ba..1c3fc2252 100644 --- a/src/common/platform/sysLinuxDbg.cpp +++ b/src/common/platform/sysLinuxDbg.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -17,56 +16,6 @@ #include // POSIX basename() lives here on macOS, not in #endif -// Avoid unwinding a guest-owned stack. -static bool OnOwnStack() { - const char* probe = reinterpret_cast(&probe); - - pthread_attr_t attr {}; -#if defined(__APPLE__) - const auto* top = static_cast(pthread_get_stackaddr_np(pthread_self())); - const auto size = pthread_get_stacksize_np(pthread_self()); - (void)attr; - return top != nullptr && size != 0 && probe < top && probe >= top - size; -#else - if (pthread_getattr_np(pthread_self(), &attr) != 0) { - return false; - } - void* base = nullptr; - size_t size = 0; - const bool ok = pthread_attr_getstack(&attr, &base, &size) == 0 && base != nullptr && size != 0; - pthread_attr_destroy(&attr); - if (!ok) { - return false; - } - const auto* low = static_cast(base); - return probe >= low && probe < low + size; -#endif -} - -void SysStackWalk(void** stack, int* depth) { - if (stack == nullptr || depth == nullptr || *depth <= 0) { - if (depth != nullptr) { - *depth = 0; - } - return; - } - - if (!OnOwnStack()) { - *depth = 0; - return; - } - - const int n = ::backtrace(stack, *depth); - *depth = (n < 0 ? 0 : n); -} - -void SysStackUsagePrint(sys_dbg_stack_info_t& stack) { - printf("stack: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast(stack.commited_addr), - static_cast(stack.commited_size)); - printf("code: (0x%" PRIx64 ", %" PRIu64 ")\n", static_cast(stack.code_addr), - static_cast(stack.code_size)); -} - void SysStackUsage(sys_dbg_stack_info_t& s) { pid_t pid = getpid(); diff --git a/src/common/platform/sysWindowsDbg.cpp b/src/common/platform/sysWindowsDbg.cpp index d0e0264a6..89e26992c 100644 --- a/src/common/platform/sysWindowsDbg.cpp +++ b/src/common/platform/sysWindowsDbg.cpp @@ -15,18 +15,6 @@ #include "common/assert.h" #include "common/platform/sysDbg.h" -void SysStackWalk(void** stack, int* depth) { - *depth = static_cast(CaptureStackBackTrace(0, static_cast(*depth), stack, nullptr)); -} - -void SysStackUsagePrint(sys_dbg_stack_info_t& stack) { - printf("stack: (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 - ", %" PRIu64 ")\n", - static_cast(stack.reserved_addr), static_cast(stack.reserved_size), - static_cast(stack.guard_addr), static_cast(stack.guard_size), - static_cast(stack.commited_addr), static_cast(stack.commited_size)); -} - void SysStackUsage(sys_dbg_stack_info_t& s) { MEMORY_BASIC_INFORMATION mbi {}; [[maybe_unused]] size_t ss = VirtualQuery(&mbi, &mbi, sizeof(mbi)); diff --git a/src/common/threads.cpp b/src/common/threads.cpp index 4f2c38221..7a034a76b 100644 --- a/src/common/threads.cpp +++ b/src/common/threads.cpp @@ -1,7 +1,6 @@ #include "common/threads.h" #include "common/assert.h" -#include "common/debug.h" #include #include diff --git a/src/graphics/host_gpu/pageManager.cpp b/src/graphics/host_gpu/pageManager.cpp index 8f596e803..65af9f2b0 100644 --- a/src/graphics/host_gpu/pageManager.cpp +++ b/src/graphics/host_gpu/pageManager.cpp @@ -1,6 +1,5 @@ #include "graphics/host_gpu/pageManager.h" -#include "common/platform/sysDbg.h" #include "graphics/host_gpu/regionDefinitions.h" #include "kernel/memory.h" @@ -21,10 +20,7 @@ #include #undef min #undef max -#elif defined(__APPLE__) -#include #else -#include #include #endif @@ -53,21 +49,6 @@ constexpr uint32_t READ_WRITE_PROTECTION = PAGE_READWRITE; std::fputs("PageManager fail-fast: ", stderr); std::fputs(reason != nullptr ? reason : "invalid page state", stderr); std::fputc('\n', stderr); -#if !defined(__APPLE__) - void* frames[16] {}; - int frame_count = static_cast(std::size(frames)); - SysStackWalk(frames, &frame_count); -#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS - const auto image_base = reinterpret_cast(GetModuleHandleW(nullptr)); - for (int i = 0; i < frame_count; i++) { - const auto address = reinterpret_cast(frames[i]); - std::fprintf(stderr, " frame[%d]=0x%016" PRIxPTR " image_rva=0x%016" PRIxPTR "\n", i, - address, address >= image_base ? address - image_base : 0); - } -#else - ::backtrace_symbols_fd(frames, frame_count, STDERR_FILENO); -#endif -#endif std::fflush(stderr); #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS TerminateProcess(GetCurrentProcess(), static_cast(EXCEPTION_NONCONTINUABLE_EXCEPTION)); diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp index c6b0e2834..9408774dd 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp @@ -1,6 +1,6 @@ #include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h" -#include "common/debug.h" +#include "common/assert.h" #include #include diff --git a/src/loader/runtimeLinker.cpp b/src/loader/runtimeLinker.cpp index fa4343ba2..2a607c04a 100644 --- a/src/loader/runtimeLinker.cpp +++ b/src/loader/runtimeLinker.cpp @@ -801,6 +801,42 @@ static bool KytyExceptionHandler(const Common::HostException::ExceptionInfo& exc return true; } } + // Report whatever guest context can be read safely before terminating: which guest thread + // faulted, the register file, the faulting code bytes and the top of its stack. + { + char thread_name[64] = "(host thread)"; + if (auto self = Libs::LibKernel::PthreadSelfOrNull(); self != nullptr) { + if (Libs::LibKernel::PthreadGetname(self, thread_name) != 0) { + std::snprintf(thread_name, sizeof(thread_name), "(unnamed guest thread)"); + } + } + std::printf("--- Guest fault context ---\n"); + std::printf("thread: %s\n", thread_name); + std::printf("rax=%016" PRIx64 " rbx=%016" PRIx64 " rcx=%016" PRIx64 " rdx=%016" PRIx64 "\n" + "rsi=%016" PRIx64 " rdi=%016" PRIx64 " rbp=%016" PRIx64 " rsp=%016" PRIx64 "\n" + "r8 =%016" PRIx64 " r9 =%016" PRIx64 " r10=%016" PRIx64 " r11=%016" PRIx64 "\n" + "r12=%016" PRIx64 " r13=%016" PRIx64 " r14=%016" PRIx64 " r15=%016" PRIx64 "\n", + info->rax, info->rbx, info->rcx, info->rdx, info->rsi, info->rdi, info->rbp, + info->rsp, info->r8, info->r9, info->r10, info->r11, info->r12, info->r13, + info->r14, info->r15); + if (IsReadableRange(info->exception_address - 48, 96)) { + const auto* code = reinterpret_cast(info->exception_address - 48); + std::printf("code (pc-48 .. pc+48, fault at byte 48):"); + for (int i = 0; i < 96; i++) { + std::printf("%s%02x", (i % 16 == 0) ? "\n " : " ", code[i]); + } + std::printf("\n"); + } + if (IsReadableRange(info->rsp, 32 * sizeof(uint64_t))) { + const auto* stack = reinterpret_cast(info->rsp); + std::printf("stack:"); + for (int i = 0; i < 32; i++) { + std::printf("%s %016" PRIx64, (i % 4 == 0) ? "\n " : "", stack[i]); + } + std::printf("\n"); + } + std::fflush(stdout); + } EXIT("Unhandled host exception: type=%u code=%u pc=0x%016" PRIx64 " access=%u address=0x%016" PRIx64 "\n", static_cast(info->type), info->native_code, info->exception_address, diff --git a/src/main.cpp b/src/main.cpp index 9248f7fc9..b800185be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,7 +3,6 @@ #include "common/debug.h" #include "common/file.h" #include "common/magicEnum.h" -#include "common/platform/sysDbg.h" #include "common/stringUtils.h" #include "common/threads.h" #include "common/virtualMemory.h"