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