summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2021-08-26 17:41:14 -0700
committerGravatar GitHub2021-08-26 17:41:14 -0700
commitd10d480642d82082e84004141e345631f308f34b (patch)
treec57b894d8896374962934c71f19c9c214c72a44b /src
parentMerge pull request #6922 from yuzu-emu/revert-6832-scheduler-improvements (diff)
parentlogging: Display backtrace on crash (diff)
downloadyuzu-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.txt3
-rw-r--r--src/common/logging/backend.cpp112
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)
176else() 176else()
177 target_link_libraries(common PRIVATE zstd) 177 target_link_libraries(common PRIVATE zstd)
178endif() 178endif()
179if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND CMAKE_CXX_COMPILER_ID STREQUAL GNU)
180 target_link_libraries(common PRIVATE backtrace)
181endif()
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
156bool initialization_in_progress_suppress_logging = true; 164bool 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