summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qdebug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qdebug.cpp')
-rw-r--r--src/corelib/io/qdebug.cpp405
1 files changed, 323 insertions, 82 deletions
diff --git a/src/corelib/io/qdebug.cpp b/src/corelib/io/qdebug.cpp
index 1889a4c3a2..64b693fea5 100644
--- a/src/corelib/io/qdebug.cpp
+++ b/src/corelib/io/qdebug.cpp
@@ -1,76 +1,35 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifdef QT_NO_DEBUG
-#undef QT_NO_DEBUG
-#endif
-#ifdef qDebug
-#undef qDebug
-#endif
+// Copyright (C) 2016 The Qt Company Ltd.
+// 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
#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
- characters in \a data.
+ characters in \a data. The size, \a len, is a 64-bit quantity to
+ avoid truncation due to implicit conversions in callers.
*/
-QByteArray QtDebugUtils::toPrintable(const char *data, int len, int maxSize)
+QByteArray QtDebugUtils::toPrintable(const char *data, qint64 len, qsizetype maxSize)
{
if (!data)
return "(null)";
QByteArray out;
- for (int i = 0; i < qMin(len, maxSize); ++i) {
+ for (qsizetype i = 0; i < qMin(len, maxSize); ++i) {
char c = data[i];
- if (isprint(c)) {
+ if (isAsciiPrintable(c)) {
out += c;
} else {
switch (c) {
@@ -192,15 +151,15 @@ QByteArray QtDebugUtils::toPrintable(const char *data, int len, int maxSize)
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(QLatin1Char(' ')))
+ 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;
@@ -222,7 +181,7 @@ void QDebug::putUcs4(uint ucs4)
stream->ts << "\\u" << qSetFieldWidth(4);
else
stream->ts << "\\U" << qSetFieldWidth(8);
- stream->ts << Qt::hex << qSetPadChar(QLatin1Char('0')) << ucs4 << Qt::reset;
+ stream->ts << Qt::hex << qSetPadChar(u'0') << ucs4 << Qt::reset;
}
maybeQuote('\'');
}
@@ -230,17 +189,15 @@ 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, int length, bool isUnicode = true)
+static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, size_t length, bool isUnicode = true)
{
- QChar quote(QLatin1Char('"'));
+ QChar quote(u'"');
d->write(&quote, 1);
bool lastWasHexEscape = false;
@@ -250,7 +207,7 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, in
if (Q_UNLIKELY(lastWasHexEscape)) {
if (fromHex(*p) != -1) {
// yes, insert it
- QChar quotes[] = { QLatin1Char('"'), QLatin1Char('"') };
+ QChar quotes[] = { quote, quote };
d->write(quotes, 2);
}
lastWasHexEscape = false;
@@ -258,7 +215,7 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, in
if (sizeof(Char) == sizeof(QChar)) {
// Surrogate characters are category Cs (Other_Surrogate), so isPrintable = false for them
- int runLength = 0;
+ qsizetype runLength = 0;
while (p + runLength != end &&
isPrintable(p[runLength]) && p[runLength] != '\\' && p[runLength] != '"')
++runLength;
@@ -274,8 +231,8 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, in
}
// print as an escape sequence (maybe, see below for surrogate pairs)
- int buflen = 2;
- ushort buf[sizeof "\\U12345678" - 1];
+ qsizetype buflen = 2;
+ char16_t buf[std::char_traits<char>::length("\\U12345678")];
buf[0] = '\\';
switch (*p) {
@@ -311,7 +268,7 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, in
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];
@@ -334,8 +291,8 @@ static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, in
// 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;
@@ -355,12 +312,12 @@ void QDebug::putString(const QChar *begin, size_t length)
if (stream->noQuotes) {
// no quotes, write the string directly too (no pretty-printing)
// this respects the QTextStream state, though
- stream->ts.d_ptr->putString(begin, int(length));
+ stream->ts.d_ptr->putString(begin, qsizetype(length));
} else {
// 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), int(length));
+ putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const char16_t *>(begin), length);
}
}
@@ -373,18 +330,189 @@ void QDebug::putByteArray(const char *begin, size_t length, Latin1Content conten
if (stream->noQuotes) {
// no quotes, write the string directly too (no pretty-printing)
// this respects the QTextStream state, though
- QString string = content == ContainsLatin1 ? QString::fromLatin1(begin, int(length)) : QString::fromUtf8(begin, int(length));
+ QString string = content == ContainsLatin1 ? QString::fromLatin1(begin, qsizetype(length))
+ : QString::fromUtf8(begin, qsizetype(length));
stream->ts.d_ptr->putString(string);
} else {
// 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 uchar *>(begin),
- int(length), content == ContainsLatin1);
+ length, content == ContainsLatin1);
+ }
+}
+
+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
@@ -460,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
@@ -728,7 +879,7 @@ QDebug &QDebug::resetFormat()
*/
/*!
- \fn QDebug &QDebug::operator<<(QLatin1String t)
+ \fn QDebug &QDebug::operator<<(QLatin1StringView t)
Writes the string, \a t, to the stream and returns a reference to the
stream. Normally, QDebug prints the string inside quotes and transforms
@@ -798,6 +949,53 @@ QDebug &QDebug::resetFormat()
*/
/*!
+ \since 6.5
+ \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)
+
+ Writes the string or string-view \a s 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.6
+ \fn template <typename Rep, typename Period> QDebug &QDebug::operator<<(std::chrono::duration<Rep, Period> duration)
+
+ 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}.
+
+ The unit is not localized.
+*/
+
+/*!
+ \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
+
+ Prints the textual representation of the 128-bit integer \a i.
+
+ \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.
+
+ \note Because the operator is a function template, no implicit conversions
+ are performed on its argument. It must be exactly qint128/quint128.
+
+ \sa QT_SUPPORTS_INT128
+*/
+
+/*!
\fn template <class T> QString QDebug::toString(T &&object)
\since 6.0
@@ -856,6 +1054,14 @@ QDebug &QDebug::resetFormat()
*/
/*!
+ \fn template <class Key, class T> QDebug operator<<(QDebug debug, const QMultiMap<Key, T> &map)
+ \relates QDebug
+
+ Writes the contents of \a map to \a debug. Both \c Key and
+ \c T need to support streaming into QDebug.
+*/
+
+/*!
\fn template <typename Key, typename T, typename Compare, typename Alloc> QDebug operator<<(QDebug debug, const std::map<Key, T, Compare, Alloc> &map)
\relates QDebug
\since 5.7
@@ -882,7 +1088,15 @@ QDebug &QDebug::resetFormat()
*/
/*!
- \fn template <class T1, class T2> QDebug operator<<(QDebug debug, const QPair<T1, T2> &pair)
+ \fn template <class Key, class T> QDebug operator<<(QDebug debug, const QMultiHash<Key, T> &hash)
+ \relates QDebug
+
+ Writes the contents of \a hash to \a debug. Both \c Key and
+ \c T need to support streaming into QDebug.
+*/
+
+/*!
+ \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
@@ -890,6 +1104,23 @@ QDebug &QDebug::resetFormat()
*/
/*!
+ \since 6.7
+ \fn template <class T> QDebug operator<<(QDebug debug, const std::optional<T> &opt)
+ \relates QDebug
+
+ Writes the contents of \a opt (or \c nullopt if not set) to \a debug.
+ \c T needs to support streaming into QDebug.
+*/
+
+/*!
+ \fn template <typename T> QDebug operator<<(QDebug debug, const QContiguousCache<T> &cache)
+ \relates QDebug
+
+ Writes the contents of \a cache to \a debug. \c T needs to
+ support streaming into QDebug.
+*/
+
+/*!
\fn template<typename T> QDebug operator<<(QDebug debug, const QFlags<T> &flags)
\relates QDebug
\since 4.7
@@ -914,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.
@@ -929,6 +1167,10 @@ QDebug &QDebug::resetFormat()
so that using << Qt::hex in a QDebug operator doesn't affect other QDebug
operators.
+ QDebugStateSaver is typically used in the implementation of an operator<<() for debugging:
+
+ \snippet customtype/customtypeexample.cpp custom type streaming operator
+
\since 5.1
*/
@@ -947,7 +1189,7 @@ public:
{
const bool currentSpaces = m_stream->space;
if (currentSpaces && !m_spaces)
- if (m_stream->buffer.endsWith(QLatin1Char(' ')))
+ if (m_stream->buffer.endsWith(u' '))
m_stream->buffer.chop(1);
m_stream->space = m_spaces;
@@ -1008,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.
@@ -1055,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"::");
@@ -1124,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 << ')';