summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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 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
155bool initialization_in_progress_suppress_logging = false; 163bool 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