diff options
Diffstat (limited to 'src/corelib/io/qdebug.cpp')
-rw-r--r-- | src/corelib/io/qdebug.cpp | 340 |
1 files changed, 253 insertions, 87 deletions
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp index 61d67ecee5..64b693fea5 100644 --- a/src/corelib/io/qdebug.cpp +++ b/src/corelib/io/qdebug.cpp @@ -2,25 +2,19 @@ // Copyright (C) 2016 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#ifdef QT_NO_DEBUG -#undef QT_NO_DEBUG -#endif -#ifdef qDebug -#undef qDebug -#endif - #include "qdebug.h" #include "private/qdebug_p.h" #include "qmetaobject.h" +#include <private/qlogging_p.h> #include <private/qtextstream_p.h> #include <private/qtools_p.h> -#include <ctype.h> + +#include <array> +#include <q20chrono.h> QT_BEGIN_NAMESPACE -using QtMiscUtils::toHexUpper; -using QtMiscUtils::toHexLower; -using QtMiscUtils::fromHex; +using namespace QtMiscUtils; /* Returns a human readable representation of the first \a maxSize @@ -35,7 +29,7 @@ QByteArray QtDebugUtils::toPrintable(const char *data, qint64 len, qsizetype max QByteArray out; for (qsizetype i = 0; i < qMin(len, maxSize); ++i) { char c = data[i]; - if (isprint(c)) { + if (isAsciiPrintable(c)) { out += c; } else { switch (c) { @@ -157,15 +151,15 @@ QByteArray QtDebugUtils::toPrintable(const char *data, qint64 len, qsizetype max Flushes any pending data to be written and destroys the debug stream. */ -// Has been defined in the header / inlined before Qt 5.4 QDebug::~QDebug() { if (stream && !--stream->ref) { if (stream->space && stream->buffer.endsWith(u' ')) stream->buffer.chop(1); if (stream->message_output) { + QInternalMessageLogContext ctxt(stream->context); qt_message_output(stream->type, - stream->context, + ctxt, stream->buffer); } delete stream; @@ -195,12 +189,10 @@ void QDebug::putUcs4(uint ucs4) // These two functions return true if the character should be printed by QDebug. // For QByteArray, this is technically identical to US-ASCII isprint(); // for QString, we use QChar::isPrint, which requires a full UCS-4 decode. -static inline bool isPrintable(uint ucs4) -{ return QChar::isPrint(ucs4); } -static inline bool isPrintable(ushort uc) -{ return QChar::isPrint(uc); } +static inline bool isPrintable(char32_t ucs4) { return QChar::isPrint(ucs4); } +static inline bool isPrintable(char16_t uc) { return QChar::isPrint(uc); } static inline bool isPrintable(uchar c) -{ return c >= ' ' && c < 0x7f; } +{ return isAsciiPrintable(c); } template <typename Char> static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, size_t length, bool isUnicode = true) @@ -240,7 +232,7 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, si // print as an escape sequence (maybe, see below for surrogate pairs) qsizetype buflen = 2; - ushort buf[sizeof "\\U12345678" - 1]; + char16_t buf[std::char_traits<char>::length("\\U12345678")]; buf[0] = '\\'; switch (*p) { @@ -276,7 +268,7 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, si if (QChar::isHighSurrogate(*p)) { if ((p + 1) != end && QChar::isLowSurrogate(p[1])) { // properly-paired surrogates - uint ucs4 = QChar::surrogateToUcs4(*p, p[1]); + char32_t ucs4 = QChar::surrogateToUcs4(*p, p[1]); if (isPrintable(ucs4)) { buf[0] = *p; buf[1] = p[1]; @@ -299,8 +291,8 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, si // improperly-paired surrogates, fall through } buf[1] = 'u'; - buf[2] = toHexUpper(ushort(*p) >> 12); - buf[3] = toHexUpper(ushort(*p) >> 8); + buf[2] = toHexUpper(char16_t(*p) >> 12); + buf[3] = toHexUpper(char16_t(*p) >> 8); buf[4] = toHexUpper(*p >> 4); buf[5] = toHexUpper(*p); buflen = 6; @@ -325,7 +317,7 @@ void QDebug::putString(const QChar *begin, size_t length) // we'll reset the QTextStream formatting mechanisms, so save the state QDebugStateSaver saver(*this); stream->ts.d_ptr->params.reset(); - putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const ushort *>(begin), length); + putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const char16_t *>(begin), length); } } @@ -350,6 +342,176 @@ void QDebug::putByteArray(const char *begin, size_t length, Latin1Content conten } } +static QByteArray timeUnit(qint64 num, qint64 den) +{ + using namespace std::chrono; + using namespace q20::chrono; + + if (num == 1 && den > 1) { + // sub-multiple of seconds + char prefix = '\0'; + auto tryprefix = [&](auto d, char c) { + static_assert(decltype(d)::num == 1, "not an SI prefix"); + if (den == decltype(d)::den) + prefix = c; + }; + + // "u" should be "ยต", but debugging output is not always UTF-8-safe + tryprefix(std::milli{}, 'm'); + tryprefix(std::micro{}, 'u'); + tryprefix(std::nano{}, 'n'); + tryprefix(std::pico{}, 'p'); + tryprefix(std::femto{}, 'f'); + tryprefix(std::atto{}, 'a'); + // uncommon ones later + tryprefix(std::centi{}, 'c'); + tryprefix(std::deci{}, 'd'); + if (prefix) { + char unit[3] = { prefix, 's' }; + return QByteArray(unit, sizeof(unit) - 1); + } + } + + const char *unit = nullptr; + if (num > 1 && den == 1) { + // multiple of seconds - but we don't use SI prefixes + auto tryunit = [&](auto d, const char *name) { + static_assert(decltype(d)::period::den == 1, "not a multiple of a second"); + if (unit || num % decltype(d)::period::num) + return; + unit = name; + num /= decltype(d)::period::num; + }; + tryunit(years{}, "yr"); + tryunit(weeks{}, "wk"); + tryunit(days{}, "d"); + tryunit(hours{}, "h"); + tryunit(minutes{}, "min"); + } + if (!unit) + unit = "s"; + + if (num == 1 && den == 1) + return unit; + if (Q_UNLIKELY(num < 1 || den < 1)) + return QString::asprintf("<invalid time unit %lld/%lld>", num, den).toLatin1(); + + // uncommon units: will return something like "[2/3]s" + // strlen("[/]min") = 6 + char buf[2 * (std::numeric_limits<qint64>::digits10 + 2) + 10]; + size_t len = 0; + auto appendChar = [&](char c) { + Q_ASSERT(len < sizeof(buf)); + buf[len++] = c; + }; + auto appendNumber = [&](qint64 value) { + if (value >= 10'000 && (value % 1000) == 0) + len += qsnprintf(buf + len, sizeof(buf) - len, "%.6g", double(value)); // "1e+06" + else + len += qsnprintf(buf + len, sizeof(buf) - len, "%lld", value); + }; + appendChar('['); + appendNumber(num); + if (den != 1) { + appendChar('/'); + appendNumber(den); + } + appendChar(']'); + memcpy(buf + len, unit, strlen(unit)); + return QByteArray(buf, len + strlen(unit)); +} + +/*! + \since 6.6 + \internal + Helper to the std::chrono::duration debug streaming output. + */ +void QDebug::putTimeUnit(qint64 num, qint64 den) +{ + stream->ts << timeUnit(num, den); // ### optimize +} + +namespace { + +#ifdef QT_SUPPORTS_INT128 + +constexpr char Q_INT128_MIN_STR[] = "-170141183460469231731687303715884105728"; + +constexpr int Int128BufferSize = sizeof(Q_INT128_MIN_STR); +using Int128Buffer = std::array<char, Int128BufferSize>; + // numeric_limits<qint128>::digits10 may not exist + +static char *i128ToStringHelper(Int128Buffer &buffer, quint128 n) +{ + auto dst = buffer.data() + buffer.size(); + *--dst = '\0'; // NUL-terminate + if (n == 0) { + *--dst = '0'; // and done + } else { + while (n != 0) { + *--dst = "0123456789"[n % 10]; + n /= 10; + } + } + return dst; +} +#endif // QT_SUPPORTS_INT128 + +[[maybe_unused]] +static const char *int128Warning() +{ + const char *msg = "Qt was not compiled with int128 support."; + qWarning("%s", msg); + return msg; +} + +} // unnamed namespace + +/*! + \since 6.7 + \internal + Helper to the qint128 debug streaming output. + */ +void QDebug::putInt128([[maybe_unused]] const void *p) +{ +#ifdef QT_SUPPORTS_INT128 + Q_ASSERT(p); + qint128 i; + memcpy(&i, p, sizeof(i)); // alignment paranoia + if (i == Q_INT128_MIN) { + // -i is not representable, hardcode the result: + stream->ts << Q_INT128_MIN_STR; + } else { + Int128Buffer buffer; + auto dst = i128ToStringHelper(buffer, i < 0 ? -i : i); + if (i < 0) + *--dst = '-'; + stream->ts << dst; + } + return; +#endif // QT_SUPPORTS_INT128 + stream->ts << int128Warning(); +} + +/*! + \since 6.7 + \internal + Helper to the quint128 debug streaming output. + */ +void QDebug::putUInt128([[maybe_unused]] const void *p) +{ +#ifdef QT_SUPPORTS_INT128 + Q_ASSERT(p); + quint128 i; + memcpy(&i, p, sizeof(i)); // alignment paranoia + Int128Buffer buffer; + stream->ts << i128ToStringHelper(buffer, i); + return; +#endif // QT_SUPPORTS_INT128 + stream->ts << int128Warning(); +} + + /*! \fn QDebug::swap(QDebug &other) \since 5.0 @@ -426,6 +588,29 @@ QDebug &QDebug::resetFormat() /*! + \fn bool QDebug::quoteStrings() const + \since 6.7 + + Returns \c true if this QDebug instance will quote strings streamed into + it (which is the default). + + \sa QDebugStateSaver, quote(), noquote(), setQuoteStrings() +*/ + +/*! + \fn void QDebug::setQuoteStrings(bool b) + \since 6.7 + + Enables quoting of strings streamed into this QDebug instance if \a b is + \c true; otherwise quoting is disabled. + + The default is to quote strings. + + \sa QDebugStateSaver, quote(), noquote(), quoteStrings() +*/ + + +/*! \fn QDebug &QDebug::quote() \since 5.4 @@ -765,75 +950,49 @@ QDebug &QDebug::resetFormat() /*! \since 6.5 - \fn QDebug &QDebug::operator<<(const std::string &s) - - Converts \a s to a QUtf8StringView, - writes the result to the stream and returns - a reference to the stream. -*/ + \fn template <typename Char, typename...Args> QDebug &QDebug::operator<<(const std::basic_string<Char, Args...> &s) + \fn template <typename Char, typename...Args> QDebug &QDebug::operator<<(std::basic_string_view<Char, Args...> s) -/*! - \since 6.5 - \fn QDebug &QDebug::operator<<(std::string_view s) + Writes the string or string-view \a s to the stream and returns a reference + to the stream. - Converts \a s to a QUtf8StringView, - writes the result to the stream and returns - a reference to the stream. + These operators only participate in overload resolution if \c Char is one of + \list + \li char + \li char8_t (C++20 only) + \li char16_t + \li char32_t + \li wchar_t + \endlist */ - /*! - \since 6.5 - \fn QDebug &QDebug::operator<<(const std::wstring &s) - - Converts \a s to a QString via QString::fromStdWString(), - writes the result to the stream and returns - a reference to the stream. -*/ + \since 6.6 + \fn template <typename Rep, typename Period> QDebug &QDebug::operator<<(std::chrono::duration<Rep, Period> duration) -/*! - \since 6.5 - \fn QDebug &QDebug::operator<<(std::wstring_view s) + Prints the time duration \a duration to the stream and returns a reference + to the stream. The printed string is the numeric representation of the + period followed by the time unit, similar to what the C++ Standard Library + would produce with \c{std::ostream}. - Converts \a s to a QString via QString::fromWCharArray(), - writes the result to the stream and returns - a reference to the stream. + The unit is not localized. */ /*! - \since 6.5 - \fn QDebug &QDebug::operator<<(const std::u16string &s) + \fn template <typename T, QDebug::if_qint128<T>> QDebug::operator<<(T i) + \fn template <typename T, QDebug::if_quint128<T>> QDebug::operator<<(T i) + \since 6.7 - Converts \a s to a QStringView, - writes the result to the stream and returns - a reference to the stream. -*/ - -/*! - \since 6.5 - \fn QDebug &QDebug::operator<<(std::u16string_view s) - - Converts \a s to a QStringView, - writes the result to the stream and returns - a reference to the stream. -*/ - -/*! - \since 6.5 - \fn QDebug &QDebug::operator<<(const std::u32string &s) + Prints the textual representation of the 128-bit integer \a i. - Converts \a s to a QString via QString::fromUcs4(), - writes the result to the stream and returns - a reference to the stream. -*/ + \note This operator is only available if Qt supports 128-bit integer types. + If 128-bit integer types are available in your build, but the Qt libraries + were compiled without, the operator will print a warning instead. -/*! - \since 6.5 - \fn QDebug &QDebug::operator<<(std::u32string_view s) + \note Because the operator is a function template, no implicit conversions + are performed on its argument. It must be exactly qint128/quint128. - Converts \a s to a QString via QString::fromUcs4(), - writes the result to the stream and returns - a reference to the stream. + \sa QT_SUPPORTS_INT128 */ /*! @@ -937,7 +1096,7 @@ QDebug &QDebug::resetFormat() */ /*! - \fn template <class T1, class T2> QDebug operator<<(QDebug debug, const QPair<T1, T2> &pair) + \fn template <class T1, class T2> QDebug operator<<(QDebug debug, const std::pair<T1, T2> &pair) \relates QDebug Writes the contents of \a pair to \a debug. Both \c T1 and @@ -945,11 +1104,12 @@ QDebug &QDebug::resetFormat() */ /*! - \fn template <class T1, class T2> QDebug operator<<(QDebug debug, const std::pair<T1, T2> &pair) + \since 6.7 + \fn template <class T> QDebug operator<<(QDebug debug, const std::optional<T> &opt) \relates QDebug - Writes the contents of \a pair to \a debug. Both \c T1 and - \c T2 need to support streaming into QDebug. + Writes the contents of \a opt (or \c nullopt if not set) to \a debug. + \c T needs to support streaming into QDebug. */ /*! @@ -985,6 +1145,13 @@ QDebug &QDebug::resetFormat() */ /*! + \since 6.7 + \fn QDebug &QDebug::operator<<(std::nullopt_t) + + Writes nullopt to the stream. +*/ + +/*! \class QDebugStateSaver \inmodule QtCore \brief Convenience class for custom QDebug operators. @@ -1002,7 +1169,7 @@ QDebug &QDebug::resetFormat() QDebugStateSaver is typically used in the implementation of an operator<<() for debugging: - \snippet tools/customtype/message.cpp custom type streaming operator + \snippet customtype/customtypeexample.cpp custom type streaming operator \since 5.1 */ @@ -1083,7 +1250,6 @@ void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, int value) #ifndef QT_NO_QOBJECT /*! - \fn QDebug qt_QMetaEnum_debugOperator(QDebug &, int value, const QMetaObject *, const char *name) \internal Formats the given enum \a value for debug output. @@ -1130,7 +1296,7 @@ QDebug qt_QMetaEnum_debugOperator(QDebug &dbg, qint64 value, const QMetaObject * dbg << scope << u"::"; } - const char *key = me.valueToKey(value); + const char *key = me.valueToKey(static_cast<int>(value)); const bool scoped = me.isScoped() || verbosity & 1; if (scoped || !key) dbg << me.enumName() << (!key ? u"(" : u"::"); @@ -1199,7 +1365,7 @@ QDebug qt_QMetaEnum_flagDebugOperator(QDebug &debug, quint64 value, const QMetaO debug << '('; } - debug << me.valueToKeys(value); + debug << me.valueToKeys(static_cast<int>(value)); if (enumScope) debug << ')'; |