diff options
| author | 2021-08-26 17:41:14 -0700 | |
|---|---|---|
| committer | 2021-08-26 17:41:14 -0700 | |
| commit | d10d480642d82082e84004141e345631f308f34b (patch) | |
| tree | c57b894d8896374962934c71f19c9c214c72a44b /src | |
| parent | Merge pull request #6922 from yuzu-emu/revert-6832-scheduler-improvements (diff) | |
| parent | logging: Display backtrace on crash (diff) | |
| download | yuzu-d10d480642d82082e84004141e345631f308f34b.tar.gz yuzu-d10d480642d82082e84004141e345631f308f34b.tar.xz yuzu-d10d480642d82082e84004141e345631f308f34b.zip | |
Merge pull request #6870 from yzct12345/trace-back-stack-back-stack-back
logging: Display backtrace on crash
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 112 |
2 files changed, 114 insertions, 1 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 57922b51c..316c4dedc 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -176,3 +176,6 @@ if (MSVC) | |||
| 176 | else() | 176 | else() |
| 177 | target_link_libraries(common PRIVATE zstd) | 177 | target_link_libraries(common PRIVATE zstd) |
| 178 | endif() | 178 | endif() |
| 179 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU) | ||
| 180 | target_link_libraries(common PRIVATE backtrace) | ||
| 181 | endif() | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 949384fd3..6f1d251e1 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -13,6 +13,14 @@ | |||
| 13 | #include <windows.h> // For OutputDebugStringW | 13 | #include <windows.h> // For OutputDebugStringW |
| 14 | #endif | 14 | #endif |
| 15 | 15 | ||
| 16 | #if defined(__linux__) && defined(__GNUG__) && !defined(__clang__) | ||
| 17 | #define BOOST_STACKTRACE_USE_BACKTRACE | ||
| 18 | #include <boost/stacktrace.hpp> | ||
| 19 | #undef BOOST_STACKTRACE_USE_BACKTRACE | ||
| 20 | #include <signal.h> | ||
| 21 | #define YUZU_LINUX_GCC_BACKTRACE | ||
| 22 | #endif | ||
| 23 | |||
| 16 | #include "common/fs/file.h" | 24 | #include "common/fs/file.h" |
| 17 | #include "common/fs/fs.h" | 25 | #include "common/fs/fs.h" |
| 18 | #include "common/fs/fs_paths.h" | 26 | #include "common/fs/fs_paths.h" |
| @@ -155,6 +163,14 @@ public: | |||
| 155 | 163 | ||
| 156 | bool initialization_in_progress_suppress_logging = true; | 164 | bool initialization_in_progress_suppress_logging = true; |
| 157 | 165 | ||
| 166 | #ifdef YUZU_LINUX_GCC_BACKTRACE | ||
| 167 | [[noreturn]] void SleepForever() { | ||
| 168 | while (true) { | ||
| 169 | pause(); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | #endif | ||
| 173 | |||
| 158 | /** | 174 | /** |
| 159 | * Static state as a singleton. | 175 | * Static state as a singleton. |
| 160 | */ | 176 | */ |
| @@ -226,9 +242,66 @@ private: | |||
| 226 | while (max_logs_to_write-- && message_queue.Pop(entry)) { | 242 | while (max_logs_to_write-- && message_queue.Pop(entry)) { |
| 227 | write_logs(); | 243 | write_logs(); |
| 228 | } | 244 | } |
| 229 | })} {} | 245 | })} { |
| 246 | #ifdef YUZU_LINUX_GCC_BACKTRACE | ||
| 247 | int waker_pipefd[2]; | ||
| 248 | int done_printing_pipefd[2]; | ||
| 249 | if (pipe2(waker_pipefd, O_CLOEXEC) || pipe2(done_printing_pipefd, O_CLOEXEC)) { | ||
| 250 | abort(); | ||
| 251 | } | ||
| 252 | backtrace_thread_waker_fd = waker_pipefd[1]; | ||
| 253 | backtrace_done_printing_fd = done_printing_pipefd[0]; | ||
| 254 | std::thread([this, wait_fd = waker_pipefd[0], done_fd = done_printing_pipefd[1]] { | ||
| 255 | Common::SetCurrentThreadName("yuzu:Crash"); | ||
| 256 | for (u8 ignore = 0; read(wait_fd, &ignore, 1) != 1;) | ||
| 257 | ; | ||
| 258 | const int sig = received_signal; | ||
| 259 | if (sig <= 0) { | ||
| 260 | abort(); | ||
| 261 | } | ||
| 262 | StopBackendThread(); | ||
| 263 | const auto signal_entry = | ||
| 264 | CreateEntry(Class::Log, Level::Critical, "?", 0, "?", | ||
| 265 | fmt::vformat("Received signal {}", fmt::make_format_args(sig))); | ||
| 266 | ForEachBackend([&signal_entry](Backend& backend) { | ||
| 267 | backend.EnableForStacktrace(); | ||
| 268 | backend.Write(signal_entry); | ||
| 269 | }); | ||
| 270 | const auto backtrace = | ||
| 271 | boost::stacktrace::stacktrace::from_dump(backtrace_storage.data(), 4096); | ||
| 272 | for (const auto& frame : backtrace.as_vector()) { | ||
| 273 | auto line = boost::stacktrace::detail::to_string(&frame, 1); | ||
| 274 | if (line.empty()) { | ||
| 275 | abort(); | ||
| 276 | } | ||
| 277 | line.pop_back(); // Remove newline | ||
| 278 | const auto frame_entry = | ||
| 279 | CreateEntry(Class::Log, Level::Critical, "?", 0, "?", line); | ||
| 280 | ForEachBackend([&frame_entry](Backend& backend) { backend.Write(frame_entry); }); | ||
| 281 | } | ||
| 282 | using namespace std::literals; | ||
| 283 | const auto rip_entry = CreateEntry(Class::Log, Level::Critical, "?", 0, "?", "RIP"s); | ||
| 284 | ForEachBackend([&rip_entry](Backend& backend) { | ||
| 285 | backend.Write(rip_entry); | ||
| 286 | backend.Flush(); | ||
| 287 | }); | ||
| 288 | for (const u8 anything = 0; write(done_fd, &anything, 1) != 1;) | ||
| 289 | ; | ||
| 290 | // Abort on original thread to help debugging | ||
| 291 | SleepForever(); | ||
| 292 | }).detach(); | ||
| 293 | signal(SIGSEGV, &HandleSignal); | ||
| 294 | signal(SIGABRT, &HandleSignal); | ||
| 295 | #endif | ||
| 296 | } | ||
| 230 | 297 | ||
| 231 | ~Impl() { | 298 | ~Impl() { |
| 299 | #ifdef YUZU_LINUX_GCC_BACKTRACE | ||
| 300 | if (int zero_or_ignore = 0; | ||
| 301 | !received_signal.compare_exchange_strong(zero_or_ignore, SIGKILL)) { | ||
| 302 | SleepForever(); | ||
| 303 | } | ||
| 304 | #endif | ||
| 232 | StopBackendThread(); | 305 | StopBackendThread(); |
| 233 | } | 306 | } |
| 234 | 307 | ||
| @@ -267,6 +340,36 @@ private: | |||
| 267 | delete ptr; | 340 | delete ptr; |
| 268 | } | 341 | } |
| 269 | 342 | ||
| 343 | #ifdef YUZU_LINUX_GCC_BACKTRACE | ||
| 344 | [[noreturn]] static void HandleSignal(int sig) { | ||
| 345 | signal(SIGABRT, SIG_DFL); | ||
| 346 | signal(SIGSEGV, SIG_DFL); | ||
| 347 | if (sig <= 0) { | ||
| 348 | abort(); | ||
| 349 | } | ||
| 350 | instance->InstanceHandleSignal(sig); | ||
| 351 | } | ||
| 352 | |||
| 353 | [[noreturn]] void InstanceHandleSignal(int sig) { | ||
| 354 | if (int zero_or_ignore = 0; !received_signal.compare_exchange_strong(zero_or_ignore, sig)) { | ||
| 355 | if (received_signal == SIGKILL) { | ||
| 356 | abort(); | ||
| 357 | } | ||
| 358 | SleepForever(); | ||
| 359 | } | ||
| 360 | // Don't restart like boost suggests. We want to append to the log file and not lose dynamic | ||
| 361 | // symbols. This may segfault if it unwinds outside C/C++ code but we'll just have to fall | ||
| 362 | // back to core dumps. | ||
| 363 | boost::stacktrace::safe_dump_to(backtrace_storage.data(), 4096); | ||
| 364 | std::atomic_thread_fence(std::memory_order_seq_cst); | ||
| 365 | for (const int anything = 0; write(backtrace_thread_waker_fd, &anything, 1) != 1;) | ||
| 366 | ; | ||
| 367 | for (u8 ignore = 0; read(backtrace_done_printing_fd, &ignore, 1) != 1;) | ||
| 368 | ; | ||
| 369 | abort(); | ||
| 370 | } | ||
| 371 | #endif | ||
| 372 | |||
| 270 | static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter}; | 373 | static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter}; |
| 271 | 374 | ||
| 272 | Filter filter; | 375 | Filter filter; |
| @@ -277,6 +380,13 @@ private: | |||
| 277 | std::thread backend_thread; | 380 | std::thread backend_thread; |
| 278 | MPSCQueue<Entry> message_queue{}; | 381 | MPSCQueue<Entry> message_queue{}; |
| 279 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | 382 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |
| 383 | |||
| 384 | #ifdef YUZU_LINUX_GCC_BACKTRACE | ||
| 385 | std::atomic_int received_signal{0}; | ||
| 386 | std::array<u8, 4096> backtrace_storage{}; | ||
| 387 | int backtrace_thread_waker_fd; | ||
| 388 | int backtrace_done_printing_fd; | ||
| 389 | #endif | ||
| 280 | }; | 390 | }; |
| 281 | } // namespace | 391 | } // namespace |
| 282 | 392 | ||