diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2024-02-24 17:45:21 +0100 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2024-04-18 15:42:27 +0200 |
commit | 1025ff1bf4f6fe60431c16e7ceb73cce2d4b9464 (patch) | |
tree | 7ef9e9ed292f8e2093abedb48a0c939d495b7b59 | |
parent | cba15d99f0cfd59ccc962f1d40168c4dca17776e (diff) |
QLogging: enable %{backtrace} support via <stacktrace>
C++23 gave us a standardized way to gather backtraces,
so we can use it to add cross-platform support for
%{backtrace}.
Guard the feature via a compile test; at the moment,
this is enabled it on MSVC only. GCC has experimental
support (requires linking against libstdc++exp), so it
will still fail the test.
[ChangeLog][QtCore][QDebug] Support for the %{backtrace}
expansion has been extended to the platforms supporting C++23's
<stacktrace> header (such as MSVC 2022 >= 17.4).
Change-Id: I04d58a193384a61e4f8e6fef78286d4bad98a025
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/configure.cmake | 24 | ||||
-rw-r--r-- | src/corelib/global/qlogging.cpp | 75 | ||||
-rw-r--r-- | src/corelib/global/qlogging_p.h | 23 |
3 files changed, 114 insertions, 8 deletions
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 58329d3b8f..80e6d93193 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -411,6 +411,25 @@ int main(void) } ") +# <stacktrace> +qt_config_compile_test(cxx23_stacktrace + LABEL "C++23 <stacktrace> support" + CODE +"#include <stacktrace> +#if !defined(__cpp_lib_stacktrace) +#error +#endif + +int main(void) +{ + /* BEGIN TEST: */ +const auto backtrace = std::stacktrace::current(); + /* END TEST: */ +} +" + CXX_STANDARD 23 +) + #### Features qt_feature("clock-gettime" PRIVATE @@ -600,6 +619,10 @@ qt_feature("backtrace" PRIVATE LABEL "backtrace" CONDITION UNIX AND QT_FEATURE_regularexpression AND WrapBacktrace_FOUND ) +qt_feature("cxx23_stacktrace" PRIVATE + LABEL "C++23 <stacktrace>" + CONDITION TEST_cxx23_stacktrace AND QT_FEATURE_cxx2b +) qt_feature("sharedmemory" PUBLIC SECTION "Kernel" LABEL "QSharedMemory" @@ -876,6 +899,7 @@ qt_feature("openssl-hash" PRIVATE qt_configure_add_summary_section(NAME "Qt Core") qt_configure_add_summary_entry(ARGS "backtrace") +qt_configure_add_summary_entry(ARGS "cxx23_stacktrace") qt_configure_add_summary_entry(ARGS "doubleconversion") qt_configure_add_summary_entry(ARGS "system-doubleconversion") qt_configure_add_summary_entry(ARGS "forkfd_pidfd" CONDITION LINUX) diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 71579ca08a..dec16e4a77 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -69,17 +69,19 @@ extern char *__progname; #endif -#ifndef QT_BOOTSTRAPPED -#if __has_include(<cxxabi.h>) && QT_CONFIG(backtrace) && QT_CONFIG(regularexpression) +#ifdef QLOGGING_HAVE_BACKTRACE # include <qregularexpression.h> +#endif + +#ifdef QLOGGING_USE_EXECINFO_BACKTRACE # if QT_CONFIG(dladdr) # include <dlfcn.h> # endif # include BACKTRACE_HEADER # include <cxxabi.h> -# define QLOGGING_HAVE_BACKTRACE -#endif +#endif // QLOGGING_USE_EXECINFO_BACKTRACE +#ifndef QT_BOOTSTRAPPED #if defined(Q_OS_LINUX) && (defined(__GLIBC__) || __has_include(<sys/syscall.h>)) # include <sys/syscall.h> @@ -1343,6 +1345,52 @@ void QMessagePattern::setPattern(const QString &pattern) Unfortunately, we can't know for sure if it has been. */ static constexpr int TypicalBacktraceFrameCount = 3; +static constexpr const char *QtCoreLibraryName = "Qt" QT_STRINGIFY(QT_VERSION_MAJOR) "Core"; + +#if defined(QLOGGING_USE_STD_BACKTRACE) +Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount) +{ + assert(frameCount >= 0); + backtrace = std::stacktrace::current(0, TypicalBacktraceFrameCount + frameCount); +} + +static QStringList +backtraceFramesForLogMessage(int frameCount, + const QInternalMessageLogContext::BacktraceStorage &buffer) +{ + QStringList result; + result.reserve(buffer.size()); + + const auto shouldSkipFrame = [](QByteArrayView description) + { +#if defined(_MSVC_STL_VERSION) + const auto libraryNameEnd = description.indexOf('!'); + if (libraryNameEnd != -1) { + const auto libraryName = description.first(libraryNameEnd); + if (!libraryName.contains(QtCoreLibraryName)) + return false; + } +#endif + if (description.contains("populateBacktrace")) + return true; + if (description.contains("QInternalMessageLogContext")) + return true; + if (description.contains("~QDebug")) + return true; + return false; + }; + + for (const auto &entry : buffer) { + const std::string description = entry.description(); + if (result.isEmpty() && shouldSkipFrame(description)) + continue; + result.append(QString::fromStdString(description)); + } + + return result; +} + +#elif defined(QLOGGING_USE_EXECINFO_BACKTRACE) Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount) { @@ -1369,7 +1417,7 @@ backtraceFramesForLogMessage(int frameCount, return result; auto shouldSkipFrame = [&result](const auto &library, const auto &function) { - if (!result.isEmpty() || !library.contains("Qt6Core"_L1)) + if (!result.isEmpty() || !library.contains(QLatin1StringView(QtCoreLibraryName))) return false; if (function.isEmpty()) return true; @@ -1476,6 +1524,9 @@ backtraceFramesForLogMessage(int frameCount, } return result; } +#else +#error "Internal error: backtrace enabled, but no way to gather backtraces available" +#endif // QLOGGING_USE_..._BACKTRACE static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams, const QMessageLogContext &ctx) @@ -2213,8 +2264,18 @@ void qErrnoWarning(int code, const char *msg, ...) specified by the optional \c depth parameter (defaults to 5), and separated by the optional \c separator parameter (defaults to "|"). - This expansion is available only on some platforms (currently only platfoms using glibc). - Names are only known for exported functions. If you want to see the name of every function + This expansion is available only on some platforms: + + \list + \li platforms using glibc; + \li platforms shipping C++23's \c{<stacktrace>} header (requires compiling Qt in C++23 mode). + \endlist + + Depending on the platform, there are some restrictions on the function + names printed by this expansion. + + On some platforms, + names are only known for exported functions. If you want to see the name of every function in your application, make sure your application is compiled and linked with \c{-rdynamic}, or an equivalent of it. diff --git a/src/corelib/global/qlogging_p.h b/src/corelib/global/qlogging_p.h index 9df53333cb..bc331c80c0 100644 --- a/src/corelib/global/qlogging_p.h +++ b/src/corelib/global/qlogging_p.h @@ -18,7 +18,20 @@ #include <QtCore/private/qglobal_p.h> #include "qlogging.h" #include "qloggingcategory.h" -#include "qvarlengtharray.h" + +#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(regularexpression) +# if __has_include(<cxxabi.h>) && QT_CONFIG(backtrace) +# include <optional> +# include "qvarlengtharray.h" +# define QLOGGING_USE_EXECINFO_BACKTRACE +# define QLOGGING_HAVE_BACKTRACE +# elif QT_CONFIG(cxx23_stacktrace) +# include <optional> +# include <stacktrace> +# define QLOGGING_USE_STD_BACKTRACE +# define QLOGGING_HAVE_BACKTRACE +# endif +#endif // QT_BOOTSTRAPPED QT_BEGIN_NAMESPACE @@ -32,7 +45,15 @@ class QInternalMessageLogContext : public QMessageLogContext { public: static constexpr int DefaultBacktraceDepth = 32; + +#if defined(QLOGGING_USE_EXECINFO_BACKTRACE) using BacktraceStorage = QVarLengthArray<void *, DefaultBacktraceDepth>; +#elif defined(QLOGGING_USE_STD_BACKTRACE) + using BacktraceStorage = std::stacktrace; +#else + using BacktraceStorage = bool; // dummy +#endif + std::optional<BacktraceStorage> backtrace; Q_ALWAYS_INLINE QInternalMessageLogContext(const QMessageLogContext &logContext) |