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>
This commit is contained in:
Giuseppe D'Angelo 2024-02-24 17:45:21 +01:00
parent cba15d99f0
commit 1025ff1bf4
3 changed files with 114 additions and 8 deletions

View File

@ -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)

View File

@ -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.

View File

@ -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)