diff options
Diffstat (limited to 'src/corelib/global/qlogging.cpp')
-rw-r--r-- | src/corelib/global/qlogging.cpp | 952 |
1 files changed, 681 insertions, 271 deletions
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 4a80b1c5c6..301a52cd57 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -23,7 +23,6 @@ #include "qthread.h" #include "private/qloggingregistry_p.h" #include "private/qcoreapplication_p.h" -#include "private/qsimd_p.h" #include <qtcore_tracepoints_p.h> #endif #ifdef Q_OS_WIN @@ -129,12 +128,49 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -#if !defined(Q_CC_MSVC) +#ifndef QT_BOOTSTRAPPED +Q_TRACE_POINT(qtcore, qt_message_print, int type, const char *category, const char *function, const char *file, int line, const QString &message); +#endif + +/*! + \headerfile <QtLogging> + \inmodule QtCore + \title Qt Logging Types + + \brief The <QtLogging> header file defines Qt logging types, functions + and macros. + + The <QtLogging> header file contains several types, functions and + macros for logging. + + The QtMsgType enum identifies the various messages that can be generated + and sent to a Qt message handler; QtMessageHandler is a type definition for + a pointer to a function with the signature + \c {void myMessageHandler(QtMsgType, const QMessageLogContext &, const char *)}. + qInstallMessageHandler() function can be used to install the given + QtMessageHandler. QMessageLogContext class contains the line, file, and + function the message was logged at. This information is created by the + QMessageLogger class. + + <QtLogging> also contains functions that generate messages from the + given string argument: qDebug(), qInfo(), qWarning(), qCritical(), + and qFatal(). These functions call the message handler + with the given message. + + Example: + + \snippet code/src_corelib_global_qglobal.cpp 4 +*/ + +template <typename String> +#if !defined(Q_CC_MSVC_ONLY) Q_NORETURN #endif -static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message); +static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, String &&message); static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message); -static void qt_message_print(const QString &message); +static void preformattedMessageHandler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage); +static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str); static int checked_var_value(const char *varname) { @@ -150,6 +186,17 @@ static int checked_var_value(const char *varname) return ok ? value : 1; } +static bool is_fatal_count_down(QAtomicInt &n) +{ + // it's fatal if the current value is exactly 1, + // otherwise decrement if it's non-zero + + int v = n.loadRelaxed(); + while (v != 0 && !n.testAndSetRelaxed(v, v - 1, v)) + qYieldCpu(); + return v == 1; // we exited the loop, so either v == 0 or CAS succeeded to set n from v to v-1 +} + static bool isFatal(QtMsgType msgType) { if (msgType == QtFatalMsg) @@ -157,28 +204,17 @@ static bool isFatal(QtMsgType msgType) if (msgType == QtCriticalMsg) { static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS"); - - // it's fatal if the current value is exactly 1, - // otherwise decrement if it's non-zero - return fatalCriticals.loadRelaxed() && fatalCriticals.fetchAndAddRelaxed(-1) == 1; + return is_fatal_count_down(fatalCriticals); } if (msgType == QtWarningMsg || msgType == QtCriticalMsg) { static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS"); - - // it's fatal if the current value is exactly 1, - // otherwise decrement if it's non-zero - return fatalWarnings.loadRelaxed() && fatalWarnings.fetchAndAddRelaxed(-1) == 1; + return is_fatal_count_down(fatalWarnings); } return false; } -static bool isDefaultCategory(const char *category) -{ - return !category || strcmp(category, "default") == 0; -} - /*! Returns true if writing to \c stderr is supported. @@ -308,7 +344,7 @@ using namespace QtPrivate; \sa QMessageLogContext, qDebug(), qInfo(), qWarning(), qCritical(), qFatal() */ -#if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) +#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) static inline void convert_to_wchar_t_elided(wchar_t *d, size_t space, const char *s) noexcept { size_t len = qstrlen(s); @@ -329,14 +365,15 @@ static inline void convert_to_wchar_t_elided(wchar_t *d, size_t space, const cha \internal */ Q_NEVER_INLINE -static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap) +static void qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap) { QString buf = QString::vasprintf(msg, ap); qt_message_print(msgType, context, buf); - return buf; + + if (isFatal(msgType)) + qt_message_fatal(msgType, context, buf); } -#undef qDebug /*! Logs a debug message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -345,17 +382,13 @@ static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, */ void QMessageLogger::debug(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtDebugMsg, context, msg, ap); + qt_message(QtDebugMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtDebugMsg)) - qt_message_fatal(QtDebugMsg, context, message); } - -#undef qInfo /*! Logs an informational message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -365,13 +398,11 @@ void QMessageLogger::debug(const char *msg, ...) const */ void QMessageLogger::info(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtInfoMsg, context, msg, ap); + qt_message(QtInfoMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtInfoMsg)) - qt_message_fatal(QtInfoMsg, context, message); } /*! @@ -400,17 +431,12 @@ void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...) co if (!cat.isDebugEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtDebugMsg, ctxt, msg, ap); + qt_message(QtDebugMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtDebugMsg)) - qt_message_fatal(QtDebugMsg, ctxt, message); } /*! @@ -427,17 +453,12 @@ void QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc, if (!cat.isDebugEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtDebugMsg, ctxt, msg, ap); + qt_message(QtDebugMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtDebugMsg)) - qt_message_fatal(QtDebugMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -511,17 +532,12 @@ void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...) con if (!cat.isInfoEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtInfoMsg, ctxt, msg, ap); + qt_message(QtInfoMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtInfoMsg)) - qt_message_fatal(QtInfoMsg, ctxt, message); } /*! @@ -538,17 +554,12 @@ void QMessageLogger::info(QMessageLogger::CategoryFunction catFunc, if (!cat.isInfoEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtInfoMsg, ctxt, msg, ap); + qt_message(QtInfoMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtInfoMsg)) - qt_message_fatal(QtInfoMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -599,7 +610,6 @@ QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc) const #endif -#undef qWarning /*! Logs a warning message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -608,13 +618,11 @@ QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc) const */ void QMessageLogger::warning(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtWarningMsg, context, msg, ap); + qt_message(QtWarningMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtWarningMsg)) - qt_message_fatal(QtWarningMsg, context, message); } /*! @@ -629,17 +637,12 @@ void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...) if (!cat.isWarningEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtWarningMsg, ctxt, msg, ap); + qt_message(QtWarningMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtWarningMsg)) - qt_message_fatal(QtWarningMsg, ctxt, message); } /*! @@ -656,17 +659,12 @@ void QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc, if (!cat.isWarningEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtWarningMsg, ctxt, msg, ap); + qt_message(QtWarningMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtWarningMsg)) - qt_message_fatal(QtWarningMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -714,8 +712,6 @@ QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc) const #endif -#undef qCritical - /*! Logs a critical message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -724,13 +720,11 @@ QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc) const */ void QMessageLogger::critical(const char *msg, ...) const { + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtCriticalMsg, context, msg, ap); + qt_message(QtCriticalMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtCriticalMsg)) - qt_message_fatal(QtCriticalMsg, context, message); } /*! @@ -745,17 +739,12 @@ void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...) if (!cat.isCriticalEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap); + qt_message(QtCriticalMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtCriticalMsg)) - qt_message_fatal(QtCriticalMsg, ctxt, message); } /*! @@ -772,17 +761,12 @@ void QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc, if (!cat.isCriticalEnabled()) return; - QMessageLogContext ctxt; - ctxt.copyContextFrom(context); - ctxt.category = cat.categoryName(); + QInternalMessageLogContext ctxt(context, cat()); va_list ap; va_start(ap, msg); // use variable arg list - const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap); + qt_message(QtCriticalMsg, ctxt, msg, ap); va_end(ap); - - if (isFatal(QtCriticalMsg)) - qt_message_fatal(QtCriticalMsg, ctxt, message); } #ifndef QT_NO_DEBUG_STREAM @@ -831,7 +815,51 @@ QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc) const #endif -#undef qFatal +/*! + Logs a fatal message specified with format \a msg for the context \a cat. + Additional parameters, specified by \a msg, may be used. + + \since 6.5 + \sa qCFatal() +*/ +void QMessageLogger::fatal(const QLoggingCategory &cat, const char *msg, ...) const noexcept +{ + QInternalMessageLogContext ctxt(context, cat()); + + va_list ap; + va_start(ap, msg); // use variable arg list + QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, ctxt, msg, ap)); + va_end(ap); + +#ifndef Q_CC_MSVC_ONLY + Q_UNREACHABLE(); +#endif +} + +/*! + Logs a fatal message specified with format \a msg for the context returned + by \a catFunc. Additional parameters, specified by \a msg, may be used. + + \since 6.5 + \sa qCFatal() +*/ +void QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc, + const char *msg, ...) const noexcept +{ + const QLoggingCategory &cat = (*catFunc)(); + + QInternalMessageLogContext ctxt(context, cat()); + + va_list ap; + va_start(ap, msg); // use variable arg list + QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, ctxt, msg, ap)); + va_end(ap); + +#ifndef Q_CC_MSVC_ONLY + Q_UNREACHABLE(); +#endif +} + /*! Logs a fatal message specified with format \a msg. Additional parameters, specified by \a msg, may be used. @@ -840,14 +868,66 @@ QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc) const */ void QMessageLogger::fatal(const char *msg, ...) const noexcept { - QString message; - + QInternalMessageLogContext ctxt(context); va_list ap; va_start(ap, msg); // use variable arg list - QT_TERMINATE_ON_EXCEPTION(message = qt_message(QtFatalMsg, context, msg, ap)); + QT_TERMINATE_ON_EXCEPTION(qt_message(QtFatalMsg, ctxt, msg, ap)); va_end(ap); - qt_message_fatal(QtFatalMsg, context, message); +#ifndef Q_CC_MSVC_ONLY + Q_UNREACHABLE(); +#endif +} + +#ifndef QT_NO_DEBUG_STREAM +/*! + Logs a fatal message using a QDebug stream. + + \since 6.5 + + \sa qFatal(), QDebug +*/ +QDebug QMessageLogger::fatal() const +{ + QDebug dbg = QDebug(QtFatalMsg); + QMessageLogContext &ctxt = dbg.stream->context; + ctxt.copyContextFrom(context); + return dbg; +} + +/*! + Logs a fatal message into category \a cat using a QDebug stream. + + \since 6.5 + \sa qCFatal(), QDebug +*/ +QDebug QMessageLogger::fatal(const QLoggingCategory &cat) const +{ + QDebug dbg = QDebug(QtFatalMsg); + + QMessageLogContext &ctxt = dbg.stream->context; + ctxt.copyContextFrom(context); + ctxt.category = cat.categoryName(); + + return dbg; +} + +/*! + Logs a fatal message into category returned by \a catFunc using a QDebug stream. + + \since 6.5 + \sa qCFatal(), QDebug +*/ +QDebug QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc) const +{ + return fatal((*catFunc)()); +} +#endif // QT_NO_DEBUG_STREAM + +#if !defined(QT_BOOTSTRAPPED) +static bool isDefaultCategory(const char *category) +{ + return !category || strcmp(category, "default") == 0; } /*! @@ -862,15 +942,20 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) if (info.isEmpty()) return info; - int pos; + qsizetype pos; // Skip trailing [with XXX] for templates (gcc), but make // sure to not affect Objective-C message names. pos = info.size() - 1; if (info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) { while (--pos) { - if (info.at(pos) == '[') - info.truncate(pos); + if (info.at(pos) == '[') { + info.truncate(pos); + break; + } + } + if (info.endsWith(' ')) { + info.chop(1); } } @@ -884,14 +969,21 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) // canonize operator names info.replace("operator ", "operator"); + pos = -1; // remove argument list forever { int parencount = 0; - pos = info.lastIndexOf(')'); + pos = info.lastIndexOf(')', pos); if (pos == -1) { // Don't know how to parse this function name return info; } + if (info.indexOf('>', pos) != -1 + || info.indexOf(':', pos) != -1) { + // that wasn't the function argument list. + --pos; + continue; + } // find the beginning of the argument list --pos; @@ -909,7 +1001,7 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) info.truncate(++pos); if (info.at(pos - 1) == ')') { - if (info.indexOf(operator_call) == pos - (int)strlen(operator_call)) + if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call))) break; // this function returns a pointer to a function @@ -932,19 +1024,19 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) if (pos > -1) { switch (info.at(pos)) { case ')': - if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1) + if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)) + 1) pos -= 2; break; case '<': - if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1) + if (info.indexOf(operator_lessThan) == pos - qsizetype(strlen(operator_lessThan)) + 1) --pos; break; case '>': - if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1) + if (info.indexOf(operator_greaterThan) == pos - qsizetype(strlen(operator_greaterThan)) + 1) --pos; break; case '=': { - int operatorLength = (int)strlen(operator_lessThanEqual); + auto operatorLength = qsizetype(strlen(operator_lessThanEqual)); if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1) pos -= 2; else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1) @@ -988,7 +1080,7 @@ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info) break; // find the matching close - int end = pos; + qsizetype end = pos; templatecount = 1; --pos; while (pos && templatecount) { @@ -1051,6 +1143,7 @@ struct QMessagePattern int backtraceDepth; }; QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace + int maxBacktraceDepth = 0; #endif bool fromEnvironment; @@ -1084,6 +1177,7 @@ void QMessagePattern::setPattern(const QString &pattern) timeArgs.clear(); #ifdef QLOGGING_HAVE_BACKTRACE backtraceArgs.clear(); + maxBacktraceDepth = 0; #endif // scanner @@ -1151,9 +1245,9 @@ void QMessagePattern::setPattern(const QString &pattern) tokens[i] = qthreadptrTokenC; else if (lexeme.startsWith(QLatin1StringView(timeTokenC))) { tokens[i] = timeTokenC; - int spaceIdx = lexeme.indexOf(QChar::fromLatin1(' ')); + qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' ')); if (spaceIdx > 0) - timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.length() - spaceIdx - 2)); + timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.size() - spaceIdx - 2)); else timeArgs.append(QString()); } else if (lexeme.startsWith(QLatin1StringView(backtraceTokenC))) { @@ -1178,6 +1272,7 @@ void QMessagePattern::setPattern(const QString &pattern) backtraceParams.backtraceDepth = backtraceDepth; backtraceParams.backtraceSeparator = backtraceSeparator; backtraceArgs.append(backtraceParams); + maxBacktraceDepth = qMax(maxBacktraceDepth, backtraceDepth); #else error += "QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"_L1; tokens[i] = ""; @@ -1205,15 +1300,11 @@ void QMessagePattern::setPattern(const QString &pattern) inIf = false; } else { tokens[i] = emptyTokenC; - error += QStringLiteral("QT_MESSAGE_PATTERN: Unknown placeholder %1\n") - .arg(lexeme); + error += "QT_MESSAGE_PATTERN: Unknown placeholder "_L1 + lexeme + '\n'_L1; } } else { - char *literal = new char[lexeme.size() + 1]; - strncpy(literal, lexeme.toLatin1().constData(), lexeme.size()); - literal[lexeme.size()] = '\0'; - literalsVar.emplace_back(literal); - tokens[i] = literal; + using UP = std::unique_ptr<char[]>; + tokens[i] = literalsVar.emplace_back(UP(qstrdup(lexeme.toLatin1().constData()))).get(); } } if (nestedIfError) @@ -1221,35 +1312,48 @@ void QMessagePattern::setPattern(const QString &pattern) else if (inIf) error += "QT_MESSAGE_PATTERN: missing %{endif}\n"_L1; - if (!error.isEmpty()) - qt_message_print(error); + if (!error.isEmpty()) { + // remove the last '\n' because the sinks deal with that on their own + error.chop(1); + + QMessageLogContext ctx(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, + "QMessagePattern::setPattern", nullptr); + preformattedMessageHandler(QtWarningMsg, ctx, error); + } literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]); std::move(literalsVar.begin(), literalsVar.end(), &literals[0]); } -#if defined(QLOGGING_HAVE_BACKTRACE) && !defined(QT_BOOTSTRAPPED) +#if defined(QLOGGING_HAVE_BACKTRACE) // make sure the function has "Message" in the name so the function is removed /* A typical backtrace in debug mode looks like: - #0 backtraceFramesForLogMessage (frameCount=5) at qlogging.cpp:1296 - #1 formatBacktraceForLogMessage (backtraceParams=..., function=0x4040b8 "virtual void MyClass::myFunction(int)") at qlogging.cpp:1344 - #2 qFormatLogMessage (type=QtDebugMsg, context=..., str=...) at qlogging.cpp:1452 - #3 stderr_message_handler (type=QtDebugMsg, context=..., message=...) at qlogging.cpp:1744 - #4 qDefaultMessageHandler (type=QtDebugMsg, context=..., message=...) at qlogging.cpp:1795 - #5 qt_message_print (msgType=QtDebugMsg, context=..., message=...) at qlogging.cpp:1840 - #6 qt_message_output (msgType=QtDebugMsg, context=..., message=...) at qlogging.cpp:1891 - #7 QDebug::~QDebug (this=<optimized out>, __in_chrg=<optimized out>) at qdebug.h:111 + #0 QInternalMessageLogContext::populateBacktrace (this=0x7fffffffd660, frameCount=5) at qlogging.cpp:1342 + #1 QInternalMessageLogContext::QInternalMessageLogContext (logContext=..., this=<optimized out>) at qlogging_p.h:42 + #2 QDebug::~QDebug (this=0x7fffffffdac8, __in_chrg=<optimized out>) at qdebug.cpp:160 + + In release mode, the QInternalMessageLogContext constructor will be usually + inlined. Empirical testing with GCC 13 and Clang 17 suggest they do obey the + Q_ALWAYS_INLINE in that constructor even in debug mode and do inline it. + Unfortunately, we can't know for sure if it has been. */ -static constexpr int TypicalBacktraceFrameCount = 8; +static constexpr int TypicalBacktraceFrameCount = 3; -# if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) -// force skipping the frame pointer, to save the backtrace() function some work -# pragma GCC push_options -# pragma GCC optimize ("omit-frame-pointer") -# endif +Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount) +{ + assert(frameCount >= 0); + BacktraceStorage &result = backtrace.emplace(TypicalBacktraceFrameCount + frameCount); + int n = ::backtrace(result.data(), result.size()); + if (n <= 0) + result.clear(); + else + result.resize(n); +} -static QStringList backtraceFramesForLogMessage(int frameCount) +static QStringList +backtraceFramesForLogMessage(int frameCount, + const QInternalMessageLogContext::BacktraceStorage &buffer) { struct DecodedFrame { QString library; @@ -1260,12 +1364,6 @@ static QStringList backtraceFramesForLogMessage(int frameCount) if (frameCount == 0) return result; - QVarLengthArray<void *, 32> buffer(TypicalBacktraceFrameCount + frameCount); - int n = backtrace(buffer.data(), buffer.size()); - if (n <= 0) - return result; - buffer.resize(n); - auto shouldSkipFrame = [&result](const auto &library, const auto &function) { if (!result.isEmpty() || !library.contains("Qt6Core"_L1)) return false; @@ -1273,7 +1371,9 @@ static QStringList backtraceFramesForLogMessage(int frameCount) return true; if (function.contains("6QDebug"_L1)) return true; - if (function.contains("Message"_L1) || function.contains("_message"_L1)) + if (function.contains("14QMessageLogger"_L1)) + return true; + if (function.contains("17qt_message_output"_L1)) return true; return false; }; @@ -1302,7 +1402,7 @@ static QStringList backtraceFramesForLogMessage(int frameCount) // use dladdr() instead of backtrace_symbols() QString cachedLibrary; const char *cachedFname = nullptr; - auto decodeFrame = [&](const void *addr) -> DecodedFrame { + auto decodeFrame = [&](void *addr) -> DecodedFrame { Dl_info info; if (!dladdr(addr, &info)) return {}; @@ -1352,7 +1452,7 @@ static QStringList backtraceFramesForLogMessage(int frameCount) }; # endif - for (void *&addr : buffer) { + for (void *const &addr : buffer) { DecodedFrame frame = decodeFrame(addr); if (!frame.library.isEmpty()) { if (frame.function.isEmpty()) @@ -1372,31 +1472,35 @@ static QStringList backtraceFramesForLogMessage(int frameCount) } static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams, - const char *function) + const QMessageLogContext &ctx) { + // do we have a backtrace stored? + if (ctx.version <= QMessageLogContext::CurrentVersion) + return QString(); + + auto &fullctx = static_cast<const QInternalMessageLogContext &>(ctx); + if (!fullctx.backtrace.has_value()) + return QString(); + QString backtraceSeparator = backtraceParams.backtraceSeparator; int backtraceDepth = backtraceParams.backtraceDepth; - QStringList frames = backtraceFramesForLogMessage(backtraceDepth); + QStringList frames = backtraceFramesForLogMessage(backtraceDepth, *fullctx.backtrace); if (frames.isEmpty()) return QString(); // if the first frame is unknown, replace it with the context function - if (function && frames.at(0).startsWith(u'?')) - frames[0] = QString::fromUtf8(qCleanupFuncinfo(function)); + if (ctx.function && frames.at(0).startsWith(u'?')) + frames[0] = QString::fromUtf8(qCleanupFuncinfo(ctx.function)); return frames.join(backtraceSeparator); } - -# if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) -# pragma GCC pop_options -# endif #endif // QLOGGING_HAVE_BACKTRACE && !QT_BOOTSTRAPPED Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern) /*! - \relates <QtGlobal> + \relates <QtLogging> \since 5.4 Generates a formatted string out of the \a type, \a context, \a str arguments. @@ -1411,6 +1515,14 @@ Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern) */ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) { + return formatLogMessage(type, context, str); +} + +// Separate function so the default message handler can bypass the public, +// exported function above. Static functions can't get added to the dynamic +// symbol tables, so they never show up in backtrace_symbols() or equivalent. +static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) +{ QString message; const auto locker = qt_scoped_lock(QMessagePattern::mutex); @@ -1424,12 +1536,10 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con bool skip = false; -#ifndef QT_BOOTSTRAPPED int timeArgsIdx = 0; #ifdef QLOGGING_HAVE_BACKTRACE int backtraceArgsIdx = 0; #endif -#endif // we do not convert file, function, line literals to local encoding due to overhead for (int i = 0; pattern->tokens[i]; ++i) { @@ -1439,14 +1549,12 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con } else if (skip) { // we skip adding messages, but we have to iterate over // timeArgsIdx and backtraceArgsIdx anyway -#ifndef QT_BOOTSTRAPPED if (token == timeTokenC) timeArgsIdx++; #ifdef QLOGGING_HAVE_BACKTRACE else if (token == backtraceTokenC) backtraceArgsIdx++; #endif -#endif } else if (token == messageTokenC) { message.append(str); } else if (token == categoryTokenC) { @@ -1474,7 +1582,6 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con message.append(QString::fromLatin1(qCleanupFuncinfo(context.function))); else message.append("unknown"_L1); -#ifndef QT_BOOTSTRAPPED } else if (token == pidTokenC) { message.append(QString::number(QCoreApplication::applicationPid())); } else if (token == appnameTokenC) { @@ -1489,18 +1596,18 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con } else if (token == backtraceTokenC) { QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx); backtraceArgsIdx++; - message.append(formatBacktraceForLogMessage(backtraceParams, context.function)); + message.append(formatBacktraceForLogMessage(backtraceParams, context)); #endif } else if (token == timeTokenC) { QString timeFormat = pattern->timeArgs.at(timeArgsIdx); timeArgsIdx++; if (timeFormat == "process"_L1) { - quint64 ms = pattern->timer.elapsed(); - message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); + quint64 ms = pattern->timer.elapsed(); + message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); } else if (timeFormat == "boot"_L1) { // just print the milliseconds since the elapsed timer reference // like the Linux kernel does - uint ms = QDeadlineTimer::current().deadline(); + qint64 ms = QDeadlineTimer::current().deadline(); message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000))); #if QT_CONFIG(datestring) } else if (timeFormat.isEmpty()) { @@ -1509,7 +1616,6 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con message.append(QDateTime::currentDateTime().toString(timeFormat)); #endif // QT_CONFIG(datestring) } -#endif // !QT_BOOTSTRAPPED } else if (token == ifCategoryTokenC) { if (isDefaultCategory(context.category)) skip = true; @@ -1528,6 +1634,20 @@ QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, con } return message; } +#else // QT_BOOTSTRAPPED +static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str) +{ + Q_UNUSED(type); + Q_UNUSED(context); + return str; +} +#endif +#ifndef QLOGGING_HAVE_BACKTRACE +void QInternalMessageLogContext::populateBacktrace(int) +{ + Q_UNREACHABLE(); +} +#endif static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf); @@ -1545,12 +1665,13 @@ Q_CONSTINIT static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext #define QT_LOG_CODE 9000 #endif -static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &, + const QString &message) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); + QString formattedMessage = message; formattedMessage.append(u'\n'); if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) { slog2_buffer_set_config_t buffer_config; @@ -1601,13 +1722,11 @@ static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &cont #if QT_CONFIG(journald) static bool systemd_default_message_handler(QtMsgType type, const QMessageLogContext &context, - const QString &message) + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - int priority = LOG_INFO; // Informational switch (type) { case QtDebugMsg: @@ -1640,13 +1759,12 @@ static bool systemd_default_message_handler(QtMsgType type, #endif #if QT_CONFIG(syslog) -static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - int priority = LOG_INFO; // Informational switch (type) { case QtDebugMsg: @@ -1675,13 +1793,11 @@ static bool syslog_default_message_handler(QtMsgType type, const QMessageLogCont #ifdef Q_OS_ANDROID static bool android_default_message_handler(QtMsgType type, const QMessageLogContext &context, - const QString &message) + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - android_LogPriority priority = ANDROID_LOG_DEBUG; switch (type) { case QtDebugMsg: @@ -1733,13 +1849,13 @@ static void win_outputDebugString_helper(const QString &message) } } -static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static bool win_message_handler(QtMsgType, const QMessageLogContext &, + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - const QString formattedMessage = qFormatLogMessage(type, context, message).append(u'\n'); - win_outputDebugString_helper(formattedMessage); + win_outputDebugString_helper(formattedMessage + u'\n'); return true; // Prevent further output to stderr } @@ -1747,15 +1863,14 @@ static bool win_message_handler(QtMsgType type, const QMessageLogContext &contex #ifdef Q_OS_WASM static bool wasm_default_message_handler(QtMsgType type, - const QMessageLogContext &context, - const QString &message) + const QMessageLogContext &, + const QString &formattedMessage) { if (shouldLogToStderr()) return false; // Leave logging up to stderr handler - QString formattedMessage = qFormatLogMessage(type, context, message); - int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE); - QByteArray localMsg = message.toLocal8Bit(); + int emOutputFlags = EM_LOG_CONSOLE; + QByteArray localMsg = formattedMessage.toLocal8Bit(); switch (type) { case QtDebugMsg: break; @@ -1780,24 +1895,61 @@ static bool wasm_default_message_handler(QtMsgType type, // -------------------------------------------------------------------------- -static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) +static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage) { - QString formattedMessage = qFormatLogMessage(type, context, message); + Q_UNUSED(type); + Q_UNUSED(context); // print nothing if message pattern didn't apply / was empty. // (still print empty lines, e.g. because message itself was empty) if (formattedMessage.isNull()) return; + fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData()); + fflush(stderr); +} -#ifdef Q_OS_WASM - // Prevent thread cross-talk, which causes Emscripten to log - // non-valid UTF-8. FIXME: remove once we upgrade to emsdk > 2.0.30 - Q_CONSTINIT static QBasicMutex m; - auto locker = qt_unique_lock(m); +namespace { +struct SystemMessageSink +{ + using Fn = bool(QtMsgType, const QMessageLogContext &, const QString &); + Fn *sink; + bool messageIsUnformatted = false; +}; +} + +static constexpr SystemMessageSink systemMessageSink = { +#if defined(QT_BOOTSTRAPPED) + nullptr +#elif defined(Q_OS_WIN) + win_message_handler +#elif QT_CONFIG(slog2) + slog2_default_handler +#elif QT_CONFIG(journald) + systemd_default_message_handler +#elif QT_CONFIG(syslog) + syslog_default_message_handler +#elif defined(Q_OS_ANDROID) + android_default_message_handler +#elif defined(QT_USE_APPLE_UNIFIED_LOGGING) + AppleUnifiedLogger::messageHandler, true +#elif defined Q_OS_WASM + wasm_default_message_handler +#else + nullptr #endif +}; - fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData()); - fflush(stderr); +static void preformattedMessageHandler(QtMsgType type, const QMessageLogContext &context, + const QString &formattedMessage) +{ +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Waddress") // "the address of ~~ will never be NULL + if (systemMessageSink.sink && systemMessageSink.sink(type, context, formattedMessage)) + return; +QT_WARNING_POP + + stderr_message_handler(type, context, formattedMessage); } /*! @@ -1806,37 +1958,24 @@ static void stderr_message_handler(QtMsgType type, const QMessageLogContext &con static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) { - bool handledStderr = false; - // A message sink logs the message to a structured or unstructured destination, // optionally formatting the message if the latter, and returns true if the sink // handled stderr output as well, which will shortcut our default stderr output. - // In the future, if we allow multiple/dynamic sinks, this will be iterating - // a list of sinks. -#if !defined(QT_BOOTSTRAPPED) -# if defined(Q_OS_WIN) - handledStderr |= win_message_handler(type, context, message); -# elif QT_CONFIG(slog2) - handledStderr |= slog2_default_handler(type, context, message); -# elif QT_CONFIG(journald) - handledStderr |= systemd_default_message_handler(type, context, message); -# elif QT_CONFIG(syslog) - handledStderr |= syslog_default_message_handler(type, context, message); -# elif defined(Q_OS_ANDROID) - handledStderr |= android_default_message_handler(type, context, message); -# elif defined(QT_USE_APPLE_UNIFIED_LOGGING) - handledStderr |= AppleUnifiedLogger::messageHandler(type, context, message); -# elif defined Q_OS_WASM - handledStderr |= wasm_default_message_handler(type, context, message); -# endif -#endif + if (systemMessageSink.messageIsUnformatted) { + if (systemMessageSink.sink(type, context, message)) + return; + } - if (!handledStderr) - stderr_message_handler(type, context, message); + preformattedMessageHandler(type, context, formatLogMessage(type, context, message)); } -#if defined(Q_COMPILER_THREAD_LOCAL) +#if defined(QT_BOOTSTRAPPED) +// there's no message handler in bootstrapped mode; force everything to stderr +static bool grabMessageHandler() { return false; } +static void ungrabMessageHandler() { } + +#elif defined(Q_COMPILER_THREAD_LOCAL) Q_CONSTINIT static thread_local bool msgHandlerGrabbed = false; @@ -1880,25 +2019,14 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex auto msgHandler = messageHandler.loadAcquire(); (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message); } else { - fprintf(stderr, "%s\n", message.toLocal8Bit().constData()); + stderr_message_handler(msgType, context, message); } } -static void qt_message_print(const QString &message) +template <typename String> +static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, String &&message) { -#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) - if (!shouldLogToStderr()) { - win_outputDebugString_helper(message); - return; - } -#endif - fprintf(stderr, "%s", message.toLocal8Bit().constData()); - fflush(stderr); -} - -static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message) -{ -#if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) +#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) wchar_t contextFileL[256]; // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to // be const wchar_t * in the first place, but the #ifdefery above is very complex and we @@ -1917,21 +2045,24 @@ static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const _CrtDbgBreak(); #else Q_UNUSED(context); - Q_UNUSED(message); #endif + if constexpr (std::is_class_v<String> && !std::is_const_v<String>) + message.clear(); + else + Q_UNUSED(message); qAbort(); } - /*! \internal */ void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message) { - qt_message_print(msgType, context, message); + QInternalMessageLogContext ctx(context); + qt_message_print(msgType, ctx, message); if (isFatal(msgType)) - qt_message_fatal(msgType, context, message); + qt_message_fatal(msgType, ctx, message); } void qErrnoWarning(const char *msg, ...) @@ -1946,8 +2077,8 @@ void qErrnoWarning(const char *msg, ...) va_end(ap); buf += " ("_L1 + error_string + u')'; - QMessageLogContext context; - qt_message_output(QtCriticalMsg, context, buf); + QInternalMessageLogContext context{QMessageLogContext()}; + qt_message_output(QtWarningMsg, context, buf); } void qErrnoWarning(int code, const char *msg, ...) @@ -1960,13 +2091,13 @@ void qErrnoWarning(int code, const char *msg, ...) va_end(ap); buf += " ("_L1 + qt_error_string(code) + u')'; - QMessageLogContext context; - qt_message_output(QtCriticalMsg, context, buf); + QInternalMessageLogContext context{QMessageLogContext()}; + qt_message_output(QtWarningMsg, context, buf); } /*! \typedef QtMessageHandler - \relates <QtGlobal> + \relates <QtLogging> \since 5.0 This is a typedef for a pointer to a function with the following @@ -1979,42 +2110,69 @@ void qErrnoWarning(int code, const char *msg, ...) /*! \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) - \relates <QtGlobal> + \relates <QtLogging> \since 5.0 - Installs a Qt message \a handler which has been defined - previously. Returns a pointer to the previous message handler. - - The message handler is a function that prints out debug messages, - warnings, critical and fatal error messages. The Qt library (debug - mode) contains hundreds of warning messages that are printed - when internal errors (usually invalid function arguments) - occur. Qt built in release mode also contains such warnings unless - QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during - compilation. If you implement your own message handler, you get total - control of these messages. - - The default message handler prints the message to the standard output - under X11 or to the debugger under Windows. If it is a fatal message, the - application aborts immediately after handling that message. Custom - message handlers should not attempt to exit an application on their own. - - Only one message handler can be defined, since this is usually - done on an application-wide basis to control debug output. - - To restore the message handler, call \c qInstallMessageHandler(0). - - Example: + Installs a Qt message \a handler. + Returns a pointer to the previously installed message handler. + + A message handler is a function that prints out debug, info, + warning, critical, and fatal messages from Qt's logging infrastructure. + By default, Qt uses a standard message handler that formats and + prints messages to different sinks specific to the operating system + and Qt configuration. Installing your own message handler allows you + to assume full control, and for instance log messages to the + file system. + + Note that Qt supports \l{QLoggingCategory}{logging categories} for + grouping related messages in semantic categories. You can use these + to enable or disable logging per category and \l{QtMsgType}{message type}. + As the filtering for logging categories is done even before a message + is created, messages for disabled types and categories will not reach + the message handler. + + A message handler needs to be + \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called + from different threads, in parallel. Therefore, writes to common sinks + (like a database, or a file) often need to be synchronized. + + Qt allows to enrich logging messages with further meta-information + by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN + environment variable. To keep this formatting, a custom message handler + can use \l qFormatLogMessage(). + + Try to keep the code in the message handler itself minimal, as expensive + operations might block the application. Also, to avoid recursion, any + logging messages generated in the message handler itself will be ignored. + + The message handler should always return. For + \l{QtFatalMsg}{fatal messages}, the application aborts immediately after + handling that message. + + Only one message handler can be installed at a time, for the whole application. + If there was a previous custom message handler installed, + the function will return a pointer to it. This handler can then + be later reinstalled by another call to the method. Also, calling + \c qInstallMessageHandler(nullptr) will restore the default + message handler. + + Here is an example of a message handler that logs to a local file + before calling the default handler: \snippet code/src_corelib_global_qglobal.cpp 23 + Note that the C++ standard guarantees that \c{static FILE *f} is + initialized in a thread-safe way. We can also expect \c{fprintf()} + and \c{fflush()} to be thread-safe, so no further synchronization + is necessary. + \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(), - {Debugging Techniques} + {Debugging Techniques}, qFormatLogMessage() */ /*! \fn void qSetMessagePattern(const QString &pattern) - \relates <QtGlobal> + \relates <QtLogging> \since 5.0 \brief Changes the output of the default message handler. @@ -2047,9 +2205,12 @@ void qErrnoWarning(int code, const char *msg, ...) \row \li \c{%{backtrace [depth=N] [separator="..."]}} \li A backtrace with the number of frames 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 - in your application, use \c{QMAKE_LFLAGS += -rdynamic}. + in your application, make sure your application is compiled and linked with \c{-rdynamic}, + or an equivalent of it. + When reading backtraces, take into account that frames might be missing due to inlining or tail call optimization. \endtable @@ -2092,6 +2253,7 @@ QtMessageHandler qInstallMessageHandler(QtMessageHandler h) return qDefaultMessageHandler; } +#ifndef QT_BOOTSTRAPPED void qSetMessagePattern(const QString &pattern) { const auto locker = qt_scoped_lock(QMessagePattern::mutex); @@ -2099,7 +2261,38 @@ void qSetMessagePattern(const QString &pattern) if (!qMessagePattern()->fromEnvironment) qMessagePattern()->setPattern(pattern); } +#endif + +static void copyInternalContext(QInternalMessageLogContext *self, + const QMessageLogContext &logContext) noexcept +{ + if (logContext.version == self->version) { + auto other = static_cast<const QInternalMessageLogContext *>(&logContext); + self->backtrace = other->backtrace; + } +} + +/*! + \internal + Copies context information from \a logContext into this QMessageLogContext. + Returns the number of backtrace frames that are desired. +*/ +int QInternalMessageLogContext::initFrom(const QMessageLogContext &logContext) +{ + version = CurrentVersion + 1; + copyContextFrom(logContext); + +#ifdef QLOGGING_HAVE_BACKTRACE + if (backtrace.has_value()) + return 0; // we have a stored backtrace, no need to get it again + // initializes the message pattern, if needed + if (auto pattern = qMessagePattern()) + return pattern->maxBacktraceDepth; +#endif + + return 0; +} /*! Copies context information from \a logContext into this QMessageLogContext. @@ -2115,6 +2308,8 @@ QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext this->file = logContext.file; this->line = logContext.line; this->function = logContext.function; + if (Q_UNLIKELY(version == CurrentVersion + 1)) + copyInternalContext(static_cast<QInternalMessageLogContext *>(this), logContext); return *this; } @@ -2162,4 +2357,219 @@ QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext \a lineNumber, in function \a functionName, and category \a categoryName. */ +/*! + \macro qDebug(const char *message, ...) + \relates <QtLogging> + \threadsafe + + Calls the message handler with the debug message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows the message is sent to the console, if it is a + console application; otherwise, it is sent to the debugger. On QNX, the + message is sent to slogger2. This function does nothing if \c QT_NO_DEBUG_OUTPUT + was defined during compilation. + + If you pass the function a format string and a list of arguments, + it works in similar way to the C printf() function. The format + should be a Latin-1 string. + + Example: + + \snippet code/src_corelib_global_qglobal.cpp 24 + + If you include \c <QtDebug>, a more convenient syntax is also + available: + + \snippet code/src_corelib_global_qglobal.cpp 25 + + With this syntax, the function returns a QDebug object that is + configured to use the QtDebugMsg message type. It automatically + puts a single space between each item, and outputs a newline at + the end. It supports many C++ and Qt types. + + To suppress the output at runtime, install your own message handler + with qInstallMessageHandler(). + + \sa qInfo(), qWarning(), qCritical(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qInfo(const char *message, ...) + \relates <QtLogging> + \threadsafe + \since 5.5 + + Calls the message handler with the informational message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the console, if it is a + console application; otherwise, it is sent to the debugger. On QNX the + message is sent to slogger2. This function does nothing if \c QT_NO_INFO_OUTPUT + was defined during compilation. + + If you pass the function a format string and a list of arguments, + it works in similar way to the C printf() function. The format + should be a Latin-1 string. + + Example: + + \snippet code/src_corelib_global_qglobal.cpp qInfo_printf + + If you include \c <QtDebug>, a more convenient syntax is also + available: + + \snippet code/src_corelib_global_qglobal.cpp qInfo_stream + + With this syntax, the function returns a QDebug object that is + configured to use the QtInfoMsg message type. It automatically + puts a single space between each item, and outputs a newline at + the end. It supports many C++ and Qt types. + + To suppress the output at runtime, install your own message handler + using qInstallMessageHandler(). + + \sa qDebug(), qWarning(), qCritical(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qWarning(const char *message, ...) + \relates <QtLogging> + \threadsafe + + Calls the message handler with the warning message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the debugger. + On QNX the message is sent to slogger2. + + This function takes a format string and a list of arguments, + similar to the C printf() function. The format should be a Latin-1 + string. + + Example: + \snippet code/src_corelib_global_qglobal.cpp 26 + + If you include <QtDebug>, a more convenient syntax is + also available: + + \snippet code/src_corelib_global_qglobal.cpp 27 + + This syntax inserts a space between each item, and + appends a newline at the end. + + This function does nothing if \c QT_NO_WARNING_OUTPUT was defined + during compilation. + To suppress the output at runtime, you can set + \l{QLoggingCategory}{logging rules} or register a custom + \l{QLoggingCategory::installFilter()}{filter}. + + For debugging purposes, it is sometimes convenient to let the + program abort for warning messages. This allows you then + to inspect the core dump, or attach a debugger - see also \l{qFatal()}. + To enable this, set the environment variable \c{QT_FATAL_WARNINGS} + to a number \c n. The program terminates then for the n-th warning. + That is, if the environment variable is set to 1, it will terminate + on the first call; if it contains the value 10, it will exit on the 10th + call. Any non-numeric value in the environment variable is equivalent to 1. + + \sa qDebug(), qInfo(), qCritical(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qCritical(const char *message, ...) + \relates <QtLogging> + \threadsafe + + Calls the message handler with the critical message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the debugger. + On QNX the message is sent to slogger2. + + This function takes a format string and a list of arguments, + similar to the C printf() function. The format should be a Latin-1 + string. + + Example: + \snippet code/src_corelib_global_qglobal.cpp 28 + + If you include <QtDebug>, a more convenient syntax is + also available: + + \snippet code/src_corelib_global_qglobal.cpp 29 + + A space is inserted between the items, and a newline is + appended at the end. + + To suppress the output at runtime, you can define + \l{QLoggingCategory}{logging rules} or register a custom + \l{QLoggingCategory::installFilter()}{filter}. + + For debugging purposes, it is sometimes convenient to let the + program abort for critical messages. This allows you then + to inspect the core dump, or attach a debugger - see also \l{qFatal()}. + To enable this, set the environment variable \c{QT_FATAL_CRITICALS} + to a number \c n. The program terminates then for the n-th critical + message. + That is, if the environment variable is set to 1, it will terminate + on the first call; if it contains the value 10, it will exit on the 10th + call. Any non-numeric value in the environment variable is equivalent to 1. + + \sa qDebug(), qInfo(), qWarning(), qFatal(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \macro qFatal(const char *message, ...) + \relates <QtLogging> + + Calls the message handler with the fatal message \a message. If no + message handler has been installed, the message is printed to + stderr. Under Windows, the message is sent to the debugger. + On QNX the message is sent to slogger2. + + If you are using the \b{default message handler} this function will + abort to create a core dump. On Windows, for debug builds, + this function will report a _CRT_ERROR enabling you to connect a debugger + to the application. + + This function takes a format string and a list of arguments, + similar to the C printf() function. + + Example: + \snippet code/src_corelib_global_qglobal.cpp 30 + + To suppress the output at runtime, install your own message handler + with qInstallMessageHandler(). + + \sa qDebug(), qInfo(), qWarning(), qCritical(), qInstallMessageHandler(), + {Debugging Techniques} +*/ + +/*! + \enum QtMsgType + \relates <QtLogging> + + This enum describes the messages that can be sent to a message + handler (QtMessageHandler). You can use the enum to identify and + associate the various message types with the appropriate + actions. + + \value QtDebugMsg + A message generated by the qDebug() function. + \value QtInfoMsg + A message generated by the qInfo() function. + \value QtWarningMsg + A message generated by the qWarning() function. + \value QtCriticalMsg + A message generated by the qCritical() function. + \value QtFatalMsg + A message generated by the qFatal() function. + \omitvalue QtSystemMsg + + \c QtInfoMsg was added in Qt 5.5. + + \sa QtMessageHandler, qInstallMessageHandler() +*/ + QT_END_NAMESPACE |