diff --git a/README.md b/README.md index 037bfb598..dd0271d97 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2014. +This is the source code for early-access 2015. ## Legal Notice diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 57922b51c..316c4dedc 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -176,3 +176,6 @@ if (MSVC) else() target_link_libraries(common PRIVATE zstd) endif() +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU) + target_link_libraries(common PRIVATE backtrace) +endif() diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 949384fd3..6f1d251e1 100755 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -13,6 +13,14 @@ #include // For OutputDebugStringW #endif +#if defined(__linux__) && defined(__GNUG__) && !defined(__clang__) +#define BOOST_STACKTRACE_USE_BACKTRACE +#include +#undef BOOST_STACKTRACE_USE_BACKTRACE +#include +#define YUZU_LINUX_GCC_BACKTRACE +#endif + #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/fs_paths.h" @@ -155,6 +163,14 @@ public: bool initialization_in_progress_suppress_logging = true; +#ifdef YUZU_LINUX_GCC_BACKTRACE +[[noreturn]] void SleepForever() { + while (true) { + pause(); + } +} +#endif + /** * Static state as a singleton. */ @@ -226,9 +242,66 @@ private: while (max_logs_to_write-- && message_queue.Pop(entry)) { write_logs(); } - })} {} + })} { +#ifdef YUZU_LINUX_GCC_BACKTRACE + int waker_pipefd[2]; + int done_printing_pipefd[2]; + if (pipe2(waker_pipefd, O_CLOEXEC) || pipe2(done_printing_pipefd, O_CLOEXEC)) { + abort(); + } + backtrace_thread_waker_fd = waker_pipefd[1]; + backtrace_done_printing_fd = done_printing_pipefd[0]; + std::thread([this, wait_fd = waker_pipefd[0], done_fd = done_printing_pipefd[1]] { + Common::SetCurrentThreadName("yuzu:Crash"); + for (u8 ignore = 0; read(wait_fd, &ignore, 1) != 1;) + ; + const int sig = received_signal; + if (sig <= 0) { + abort(); + } + StopBackendThread(); + const auto signal_entry = + CreateEntry(Class::Log, Level::Critical, "?", 0, "?", + fmt::vformat("Received signal {}", fmt::make_format_args(sig))); + ForEachBackend([&signal_entry](Backend& backend) { + backend.EnableForStacktrace(); + backend.Write(signal_entry); + }); + const auto backtrace = + boost::stacktrace::stacktrace::from_dump(backtrace_storage.data(), 4096); + for (const auto& frame : backtrace.as_vector()) { + auto line = boost::stacktrace::detail::to_string(&frame, 1); + if (line.empty()) { + abort(); + } + line.pop_back(); // Remove newline + const auto frame_entry = + CreateEntry(Class::Log, Level::Critical, "?", 0, "?", line); + ForEachBackend([&frame_entry](Backend& backend) { backend.Write(frame_entry); }); + } + using namespace std::literals; + const auto rip_entry = CreateEntry(Class::Log, Level::Critical, "?", 0, "?", "RIP"s); + ForEachBackend([&rip_entry](Backend& backend) { + backend.Write(rip_entry); + backend.Flush(); + }); + for (const u8 anything = 0; write(done_fd, &anything, 1) != 1;) + ; + // Abort on original thread to help debugging + SleepForever(); + }).detach(); + signal(SIGSEGV, &HandleSignal); + signal(SIGABRT, &HandleSignal); +#endif + } ~Impl() { +#ifdef YUZU_LINUX_GCC_BACKTRACE + if (int zero_or_ignore = 0; + !received_signal.compare_exchange_strong(zero_or_ignore, SIGKILL)) { + SleepForever(); + } +#endif StopBackendThread(); } @@ -267,6 +340,36 @@ private: delete ptr; } +#ifdef YUZU_LINUX_GCC_BACKTRACE + [[noreturn]] static void HandleSignal(int sig) { + signal(SIGABRT, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + if (sig <= 0) { + abort(); + } + instance->InstanceHandleSignal(sig); + } + + [[noreturn]] void InstanceHandleSignal(int sig) { + if (int zero_or_ignore = 0; !received_signal.compare_exchange_strong(zero_or_ignore, sig)) { + if (received_signal == SIGKILL) { + abort(); + } + SleepForever(); + } + // Don't restart like boost suggests. We want to append to the log file and not lose dynamic + // symbols. This may segfault if it unwinds outside C/C++ code but we'll just have to fall + // back to core dumps. + boost::stacktrace::safe_dump_to(backtrace_storage.data(), 4096); + std::atomic_thread_fence(std::memory_order_seq_cst); + for (const int anything = 0; write(backtrace_thread_waker_fd, &anything, 1) != 1;) + ; + for (u8 ignore = 0; read(backtrace_done_printing_fd, &ignore, 1) != 1;) + ; + abort(); + } +#endif + static inline std::unique_ptr instance{nullptr, Deleter}; Filter filter; @@ -277,6 +380,13 @@ private: std::thread backend_thread; MPSCQueue message_queue{}; std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; + +#ifdef YUZU_LINUX_GCC_BACKTRACE + std::atomic_int received_signal{0}; + std::array backtrace_storage{}; + int backtrace_thread_waker_fd; + int backtrace_done_printing_fd; +#endif }; } // namespace