OSDN Git Service

introduce support for backtrace on crash via Unwind
authorIvailo Monev <xakepa10@laimg.moc>
Thu, 23 Jan 2020 16:29:42 +0000 (16:29 +0000)
committerIvailo Monev <xakepa10@laimg.moc>
Thu, 23 Jan 2020 16:29:42 +0000 (16:29 +0000)
Signed-off-by: Ivailo Monev <xakepa10@laimg.moc>
.travis.yml
CMakeLists.txt
src/core/global/qglobal.cpp

index 3813641..8f1826c 100644 (file)
@@ -18,6 +18,6 @@ before_install:
 script:
     - mkdir build && cd build
     - if [ "$CXX" == "clang++" ];then cmake ../ -GNinja -DCMAKE_BUILD_TYPE=Release -DKATIE_TESTS=ON -DKATIE_BENCHMARKS=ON -DKATIE_UTILS=ON -Wno-dev ;fi
-    - if [ "$CXX" == "g++" ];then cmake ../ -GNinja -DCMAKE_BUILD_TYPE=Debug -DKATIE_TESTS=ON -DKATIE_ALLINONE=ON -Wno-dev ;fi
+    - if [ "$CXX" == "g++" ];then cmake ../ -GNinja -DCMAKE_BUILD_TYPE=Debug -DKATIE_TESTS=ON -DKATIE_ALLINONE=ON -Wno-dev -DWITH_UNWIND=ON ;fi
     - ninja
     - if [ "$CXX" == "g++" ];then ctest -V ;fi
index 6cdcff1..6eef31c 100644 (file)
@@ -476,7 +476,7 @@ set_package_properties(MySQL PROPERTIES
 
 find_package(Unwind)
 set_package_properties(Unwind PROPERTIES
-    PURPOSE "Required for stack traces on assert"
+    PURPOSE "Required for stack traces on assert and crash"
     DESCRIPTION "A (mostly) platform-independent unwind API"
     URL "https://www.nongnu.org/libunwind/index.html"
     TYPE OPTIONAL
index e03e542..d808699 100644 (file)
@@ -51,6 +51,7 @@
 #endif
 
 #ifndef QT_NO_UNWIND
+#  include "qcoreapplication.h"
 #  define UNW_LOCAL_ONLY
 #  include <libunwind.h>
 #  include <cxxabi.h>
@@ -1251,9 +1252,9 @@ void qBadAlloc()
     QT_THROW(std::bad_alloc());
 }
 
-static inline void qt_print_backtrace()
-{
 #ifndef QT_NO_UNWIND
+static void qt_print_backtrace()
+{
     unw_cursor_t cursor;
     unw_context_t context;
 
@@ -1276,15 +1277,86 @@ static inline void qt_print_backtrace()
             printf("qt_print_backtrace: unable to obtain symbol name for this frame\n");
         }
     }
+}
+
+typedef void (*QCrashHandler)(int);
+
+static bool qt_set_crash_handler(QCrashHandler handler) {
+    sigset_t mask;
+    if (::sigemptyset(&mask) != 0) {
+        ::fprintf(stderr, "sigaddset error: %s\n", ::strerror(errno));
+        return false;
+    }
+
+#define HANDLE_SIGNAL(x) \
+    ::signal(x, handler); \
+    if (::sigaddset(&mask, x) != 0) { \
+        ::fprintf(stderr, "sigaddset error: %s\n", ::strerror(errno)); \
+        return false; \
+    }
+
+#ifdef SIGSEGV
+    HANDLE_SIGNAL(SIGSEGV)
+#endif
+#ifdef SIGBUS
+    HANDLE_SIGNAL(SIGBUS)
 #endif
+#ifdef SIGFPE
+    HANDLE_SIGNAL(SIGFPE)
+#endif
+#ifdef SIGILL
+    HANDLE_SIGNAL(SIGILL)
+#endif
+#ifdef SIGABRT
+    HANDLE_SIGNAL(SIGABRT)
+#endif
+
+#undef HANDLE_SIGNAL
+
+    if (::sigprocmask(SIG_UNBLOCK, &mask, nullptr) != 0) {
+        ::fprintf(stderr, "sigprocmask error: %s\n", ::strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+static void qt_crash_handler(int sig) {
+    const QByteArray name = QCoreApplication::applicationName().toLatin1();
+    const std::string pid = std::to_string(::getpid());
+
+    if (name.isEmpty()) {
+        ::fprintf(stderr, "PID %s crashed\n", pid.c_str());
+    } else {
+        ::fprintf(stderr, "%s with PID %s crashed\n", name.constData(), pid.c_str());
+    }
+
+    qt_set_crash_handler(SIG_DFL);
+
+    qt_print_backtrace();
+
+    ::raise(sig);
 }
 
+int qt_install_crash_handler()
+{
+    qt_set_crash_handler(qt_crash_handler);
+    return 0;
+}
+
+Q_CONSTRUCTOR_FUNCTION(qt_install_crash_handler);
+#endif
+
 /*
   The Q_ASSERT macro calls this function when the test fails.
 */
 void qt_assert(const char *assertion, const char *file, int line)
 {
-    qt_print_backtrace();
+#ifndef QT_NO_UNWIND
+    // don't print backtrace twice if abort() will be called in qt_message_output()
+    if (qgetenv("QT_FATAL_WARNINGS").isNull())
+        qt_print_backtrace();
+#endif
     qFatal("ASSERT: \"%s\" in file %s, line %d", assertion, file, line);
 }
 
@@ -1293,7 +1365,11 @@ void qt_assert(const char *assertion, const char *file, int line)
 */
 void qt_assert_x(const char *where, const char *what, const char *file, int line)
 {
-    qt_print_backtrace();
+#ifndef QT_NO_UNWIND
+    // don't print backtrace twice if abort() will be called in qt_message_output()
+    if (qgetenv("QT_FATAL_WARNINGS").isNull())
+        qt_print_backtrace();
+#endif
     qFatal("ASSERT failure in %s: \"%s\", file %s, line %d", where, what, file, line);
 }