Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdebug.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qdebug.h"
6#include "private/qdebug_p.h"
7#include "qmetaobject.h"
8#include <private/qlogging_p.h>
9#include <private/qtextstream_p.h>
10#include <private/qtools_p.h>
11
12#include <array>
13#include <q20chrono.h>
14
16
17using namespace QtMiscUtils;
18
19/*
20 Returns a human readable representation of the first \a maxSize
21 characters in \a data. The size, \a len, is a 64-bit quantity to
22 avoid truncation due to implicit conversions in callers.
23*/
25{
26 if (!data)
27 return "(null)";
28
30 for (qsizetype i = 0; i < qMin(len, maxSize); ++i) {
31 char c = data[i];
32 if (isAsciiPrintable(c)) {
33 out += c;
34 } else {
35 switch (c) {
36 case '\n':
37 out += "\\n";
38 break;
39 case '\r':
40 out += "\\r";
41 break;
42 case '\t':
43 out += "\\t";
44 break;
45 default: {
46 const char buf[] = {
47 '\\',
48 'x',
49 toHexLower(uchar(c) / 16),
50 toHexLower(uchar(c) % 16),
51 0
52 };
53 out += buf;
54 }
55 }
56 }
57 }
58
59 if (maxSize < len)
60 out += "...";
61
62 return out;
63}
64
65// This file is needed to force compilation of QDebug into the kernel library.
66
154QDebug::~QDebug()
155{
156 if (stream && !--stream->ref) {
157 if (stream->space && stream->buffer.endsWith(u' '))
158 stream->buffer.chop(1);
159 if (stream->message_output) {
160 QInternalMessageLogContext ctxt(stream->context);
162 ctxt,
163 stream->buffer);
164 }
165 delete stream;
166 }
167}
168
172void QDebug::putUcs4(uint ucs4)
173{
174 maybeQuote('\'');
175 if (ucs4 < 0x20) {
176 stream->ts << "\\x" << Qt::hex << ucs4 << Qt::reset;
177 } else if (ucs4 < 0x80) {
178 stream->ts << char(ucs4);
179 } else {
180 if (ucs4 < 0x10000)
181 stream->ts << "\\u" << qSetFieldWidth(4);
182 else
183 stream->ts << "\\U" << qSetFieldWidth(8);
184 stream->ts << Qt::hex << qSetPadChar(u'0') << ucs4 << Qt::reset;
185 }
186 maybeQuote('\'');
187}
188
189// These two functions return true if the character should be printed by QDebug.
190// For QByteArray, this is technically identical to US-ASCII isprint();
191// for QString, we use QChar::isPrint, which requires a full UCS-4 decode.
192static inline bool isPrintable(char32_t ucs4) { return QChar::isPrint(ucs4); }
193static inline bool isPrintable(char16_t uc) { return QChar::isPrint(uc); }
194static inline bool isPrintable(uchar c)
195{ return isAsciiPrintable(c); }
196
197template <typename Char>
198static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, size_t length, bool isUnicode = true)
199{
200 QChar quote(u'"');
201 d->write(&quote, 1);
202
203 bool lastWasHexEscape = false;
204 const Char *end = begin + length;
205 for (const Char *p = begin; p != end; ++p) {
206 // check if we need to insert "" to break an hex escape sequence
207 if (Q_UNLIKELY(lastWasHexEscape)) {
208 if (fromHex(*p) != -1) {
209 // yes, insert it
210 QChar quotes[] = { quote, quote };
211 d->write(quotes, 2);
212 }
213 lastWasHexEscape = false;
214 }
215
216 if (sizeof(Char) == sizeof(QChar)) {
217 // Surrogate characters are category Cs (Other_Surrogate), so isPrintable = false for them
218 qsizetype runLength = 0;
219 while (p + runLength != end &&
220 isPrintable(p[runLength]) && p[runLength] != '\\' && p[runLength] != '"')
221 ++runLength;
222 if (runLength) {
223 d->write(reinterpret_cast<const QChar *>(p), runLength);
224 p += runLength - 1;
225 continue;
226 }
227 } else if (isPrintable(*p) && *p != '\\' && *p != '"') {
228 QChar c = QLatin1Char(*p);
229 d->write(&c, 1);
230 continue;
231 }
232
233 // print as an escape sequence (maybe, see below for surrogate pairs)
234 qsizetype buflen = 2;
235 char16_t buf[std::char_traits<char>::length("\\U12345678")];
236 buf[0] = '\\';
237
238 switch (*p) {
239 case '"':
240 case '\\':
241 buf[1] = *p;
242 break;
243 case '\b':
244 buf[1] = 'b';
245 break;
246 case '\f':
247 buf[1] = 'f';
248 break;
249 case '\n':
250 buf[1] = 'n';
251 break;
252 case '\r':
253 buf[1] = 'r';
254 break;
255 case '\t':
256 buf[1] = 't';
257 break;
258 default:
259 if (!isUnicode) {
260 // print as hex escape
261 buf[1] = 'x';
262 buf[2] = toHexUpper(uchar(*p) >> 4);
263 buf[3] = toHexUpper(uchar(*p));
264 buflen = 4;
265 lastWasHexEscape = true;
266 break;
267 }
268 if (QChar::isHighSurrogate(*p)) {
269 if ((p + 1) != end && QChar::isLowSurrogate(p[1])) {
270 // properly-paired surrogates
271 char32_t ucs4 = QChar::surrogateToUcs4(*p, p[1]);
272 if (isPrintable(ucs4)) {
273 buf[0] = *p;
274 buf[1] = p[1];
275 buflen = 2;
276 } else {
277 buf[1] = 'U';
278 buf[2] = '0'; // toHexUpper(ucs4 >> 32);
279 buf[3] = '0'; // toHexUpper(ucs4 >> 28);
280 buf[4] = toHexUpper(ucs4 >> 20);
281 buf[5] = toHexUpper(ucs4 >> 16);
282 buf[6] = toHexUpper(ucs4 >> 12);
283 buf[7] = toHexUpper(ucs4 >> 8);
284 buf[8] = toHexUpper(ucs4 >> 4);
285 buf[9] = toHexUpper(ucs4);
286 buflen = 10;
287 }
288 ++p;
289 break;
290 }
291 // improperly-paired surrogates, fall through
292 }
293 buf[1] = 'u';
294 buf[2] = toHexUpper(char16_t(*p) >> 12);
295 buf[3] = toHexUpper(char16_t(*p) >> 8);
296 buf[4] = toHexUpper(*p >> 4);
297 buf[5] = toHexUpper(*p);
298 buflen = 6;
299 }
300 d->write(reinterpret_cast<QChar *>(buf), buflen);
301 }
302
303 d->write(&quote, 1);
304}
305
310void QDebug::putString(const QChar *begin, size_t length)
311{
312 if (stream->noQuotes) {
313 // no quotes, write the string directly too (no pretty-printing)
314 // this respects the QTextStream state, though
315 stream->ts.d_ptr->putString(begin, qsizetype(length));
316 } else {
317 // we'll reset the QTextStream formatting mechanisms, so save the state
318 QDebugStateSaver saver(*this);
319 stream->ts.d_ptr->params.reset();
320 putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const char16_t *>(begin), length);
321 }
322}
323
328void QDebug::putByteArray(const char *begin, size_t length, Latin1Content content)
329{
330 if (stream->noQuotes) {
331 // no quotes, write the string directly too (no pretty-printing)
332 // this respects the QTextStream state, though
333 QString string = content == ContainsLatin1 ? QString::fromLatin1(begin, qsizetype(length))
335 stream->ts.d_ptr->putString(string);
336 } else {
337 // we'll reset the QTextStream formatting mechanisms, so save the state
338 QDebugStateSaver saver(*this);
339 stream->ts.d_ptr->params.reset();
340 putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const uchar *>(begin),
341 length, content == ContainsLatin1);
342 }
343}
344
346{
347 using namespace std::chrono;
348 using namespace q20::chrono;
349
350 if (num == 1 && den > 1) {
351 // sub-multiple of seconds
352 char prefix = '\0';
353 auto tryprefix = [&](auto d, char c) {
354 static_assert(decltype(d)::num == 1, "not an SI prefix");
355 if (den == decltype(d)::den)
356 prefix = c;
357 };
358
359 // "u" should be "µ", but debugging output is not always UTF-8-safe
360 tryprefix(std::milli{}, 'm');
361 tryprefix(std::micro{}, 'u');
362 tryprefix(std::nano{}, 'n');
363 tryprefix(std::pico{}, 'p');
364 tryprefix(std::femto{}, 'f');
365 tryprefix(std::atto{}, 'a');
366 // uncommon ones later
367 tryprefix(std::centi{}, 'c');
368 tryprefix(std::deci{}, 'd');
369 if (prefix) {
370 char unit[3] = { prefix, 's' };
371 return QByteArray(unit, sizeof(unit) - 1);
372 }
373 }
374
375 const char *unit = nullptr;
376 if (num > 1 && den == 1) {
377 // multiple of seconds - but we don't use SI prefixes
378 auto tryunit = [&](auto d, const char *name) {
379 static_assert(decltype(d)::period::den == 1, "not a multiple of a second");
380 if (unit || num % decltype(d)::period::num)
381 return;
382 unit = name;
383 num /= decltype(d)::period::num;
384 };
385 tryunit(years{}, "yr");
386 tryunit(weeks{}, "wk");
387 tryunit(days{}, "d");
388 tryunit(hours{}, "h");
389 tryunit(minutes{}, "min");
390 }
391 if (!unit)
392 unit = "s";
393
394 if (num == 1 && den == 1)
395 return unit;
396 if (Q_UNLIKELY(num < 1 || den < 1))
397 return QString::asprintf("<invalid time unit %lld/%lld>", num, den).toLatin1();
398
399 // uncommon units: will return something like "[2/3]s"
400 // strlen("[/]min") = 6
401 char buf[2 * (std::numeric_limits<qint64>::digits10 + 2) + 10];
402 size_t len = 0;
403 auto appendChar = [&](char c) {
404 Q_ASSERT(len < sizeof(buf));
405 buf[len++] = c;
406 };
407 auto appendNumber = [&](qint64 value) {
408 if (value >= 10'000 && (value % 1000) == 0)
409 len += qsnprintf(buf + len, sizeof(buf) - len, "%.6g", double(value)); // "1e+06"
410 else
411 len += qsnprintf(buf + len, sizeof(buf) - len, "%lld", value);
412 };
413 appendChar('[');
414 appendNumber(num);
415 if (den != 1) {
416 appendChar('/');
417 appendNumber(den);
418 }
419 appendChar(']');
420 memcpy(buf + len, unit, strlen(unit));
421 return QByteArray(buf, len + strlen(unit));
422}
423
429void QDebug::putTimeUnit(qint64 num, qint64 den)
430{
431 stream->ts << timeUnit(num, den); // ### optimize
432}
433
434namespace {
435
436#ifdef QT_SUPPORTS_INT128
437
438constexpr char Q_INT128_MIN_STR[] = "-170141183460469231731687303715884105728";
439
440constexpr int Int128BufferSize = sizeof(Q_INT128_MIN_STR);
441using Int128Buffer = std::array<char, Int128BufferSize>;
442 // numeric_limits<qint128>::digits10 may not exist
443
444static char *i128ToStringHelper(Int128Buffer &buffer, quint128 n)
445{
446 auto dst = buffer.data() + buffer.size();
447 *--dst = '\0'; // NUL-terminate
448 if (n == 0) {
449 *--dst = '0'; // and done
450 } else {
451 while (n != 0) {
452 *--dst = "0123456789"[n % 10];
453 n /= 10;
454 }
455 }
456 return dst;
457}
458#endif // QT_SUPPORTS_INT128
459
460[[maybe_unused]]
461static const char *int128Warning()
462{
463 const char *msg = "Qt was not compiled with int128 support.";
464 qWarning("%s", msg);
465 return msg;
466}
467
468} // unnamed namespace
469
475void QDebug::putInt128([[maybe_unused]] const void *p)
476{
477#ifdef QT_SUPPORTS_INT128
478 Q_ASSERT(p);
479 qint128 i;
480 memcpy(&i, p, sizeof(i)); // alignment paranoia
481 if (i == Q_INT128_MIN) {
482 // -i is not representable, hardcode the result:
483 stream->ts << Q_INT128_MIN_STR;
484 } else {
485 Int128Buffer buffer;
486 auto dst = i128ToStringHelper(buffer, i < 0 ? -i : i);
487 if (i < 0)
488 *--dst = '-';
489 stream->ts << dst;
490 }
491 return;
492#endif // QT_SUPPORTS_INT128
493 stream->ts << int128Warning();
494}
495
501void QDebug::putUInt128([[maybe_unused]] const void *p)
502{
503#ifdef QT_SUPPORTS_INT128
504 Q_ASSERT(p);
505 quint128 i;
506 memcpy(&i, p, sizeof(i)); // alignment paranoia
507 Int128Buffer buffer;
508 stream->ts << i128ToStringHelper(buffer, i);
509 return;
510#endif // QT_SUPPORTS_INT128
511 stream->ts << int128Warning();
512}
513
514
529QDebug &QDebug::resetFormat()
530{
531 stream->ts.reset();
532 stream->space = true;
533 stream->noQuotes = false;
534 stream->verbosity = DefaultVerbosity;
535 return *this;
536}
537
1178{
1179public:
1181 : m_stream(stream),
1182 m_spaces(stream->space),
1183 m_noQuotes(stream->noQuotes),
1184 m_verbosity(stream->verbosity),
1185 m_streamParams(stream->ts.d_ptr->params)
1186 {
1187 }
1189 {
1190 const bool currentSpaces = m_stream->space;
1191 if (currentSpaces && !m_spaces)
1192 if (m_stream->buffer.endsWith(u' '))
1193 m_stream->buffer.chop(1);
1194
1195 m_stream->space = m_spaces;
1196 m_stream->noQuotes = m_noQuotes;
1197 m_stream->ts.d_ptr->params = m_streamParams;
1198 m_stream->verbosity = m_verbosity;
1199
1200 if (!currentSpaces && m_spaces)
1201 m_stream->ts << ' ';
1202 }
1203
1204 QDebug::Stream *m_stream;
1205
1206 // QDebug state
1207 const bool m_spaces;
1208 const bool m_noQuotes;
1209 const int m_verbosity;
1210
1211 // QTextStream state
1213};
1214
1215
1222QDebugStateSaver::QDebugStateSaver(QDebug &dbg)
1223 : d(new QDebugStateSaverPrivate(dbg.stream))
1224{
1225}
1226
1233QDebugStateSaver::~QDebugStateSaver()
1234{
1235 d->restoreState();
1236}
1237
1247{
1248 qt_QMetaEnum_flagDebugOperator<int>(debug, sizeofT, value);
1249}
1250
1251#ifndef QT_NO_QOBJECT
1288{
1289 QDebugStateSaver saver(dbg);
1290 dbg.nospace();
1291 QMetaEnum me = meta->enumerator(meta->indexOfEnumerator(name));
1292
1293 const int verbosity = dbg.verbosity();
1294 if (verbosity >= QDebug::DefaultVerbosity) {
1295 if (const char *scope = me.scope())
1296 dbg << scope << u"::";
1297 }
1298
1299 const char *key = me.valueToKey(static_cast<int>(value));
1300 const bool scoped = me.isScoped() || verbosity & 1;
1301 if (scoped || !key)
1302 dbg << me.enumName() << (!key ? u"(" : u"::");
1303
1304 if (key)
1305 dbg << key;
1306 else
1307 dbg << value << ')';
1308
1309 return dbg;
1310}
1311
1342{
1343 const int verbosity = debug.verbosity();
1344
1345 QDebugStateSaver saver(debug);
1346 debug.resetFormat();
1347 debug.noquote();
1348 debug.nospace();
1349
1350 const QMetaEnum me = meta->enumerator(meta->indexOfEnumerator(name));
1351
1352 const bool classScope = verbosity >= QDebug::DefaultVerbosity;
1353 if (classScope) {
1354 debug << u"QFlags<";
1355
1356 if (const char *scope = me.scope())
1357 debug << scope << u"::";
1358 }
1359
1360 const bool enumScope = me.isScoped() || verbosity > QDebug::MinimumVerbosity;
1361 if (enumScope) {
1362 debug << me.enumName();
1363 if (classScope)
1364 debug << '>';
1365 debug << '(';
1366 }
1367
1368 debug << me.valueToKeys(static_cast<int>(value));
1369
1370 if (enumScope)
1371 debug << ')';
1372
1373 return debug;
1374}
1375#endif // !QT_NO_QOBJECT
1376
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
const QTextStreamPrivate::Params m_streamParams
Definition qdebug.cpp:1212
QDebug::Stream * m_stream
Definition qdebug.cpp:1204
QDebugStateSaverPrivate(QDebug::Stream *stream)
Definition qdebug.cpp:1180
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
const char * valueToKey(int value) const
Returns the string that is used as the name of the given enumeration value, or \nullptr if value is n...
QByteArray valueToKeys(int value) const
Returns a byte array of '|'-separated keys that represents the given value.
bool isScoped() const
const char * enumName() const
Returns the enum name of the flag (without the scope).
const char * scope() const
Returns the scope this enumerator was declared in.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
Combined button and popup list for selecting options.
qsizetype fromUtf8(uchar b, OutputPtr &dst, InputPtr &src, InputPtr end)
Q_CORE_EXPORT QByteArray toPrintable(const char *data, qint64 len, qsizetype maxSize)
Definition qdebug.cpp:24
constexpr int isAsciiPrintable(char32_t ch) noexcept
Definition qtools_p.h:104
constexpr char toHexUpper(char32_t value) noexcept
Definition qtools_p.h:27
constexpr char toHexLower(char32_t value) noexcept
Definition qtools_p.h:32
constexpr int fromHex(char32_t c) noexcept
Definition qtools_p.h:44
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & reset(QTextStream &stream)
Calls QTextStream::reset() on stream and returns stream.
std::chrono::duration< IntRep, std::ratio_multiply< std::ratio< 7 >, days::period > > weeks
Definition q20chrono.h:53
std::chrono::duration< IntRep, std::ratio< 86400 > > days
Definition q20chrono.h:52
std::chrono::duration< IntRep, std::ratio_multiply< std::ratio< 146097, 400 >, days::period > > years
Definition q20chrono.h:54
Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt,...)
#define Q_UNLIKELY(x)
QDebug qt_QMetaEnum_debugOperator(QDebug &dbg, qint64 value, const QMetaObject *meta, const char *name)
Definition qdebug.cpp:1287
void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, int value)
Definition qdebug.cpp:1246
static void putEscapedString(QTextStreamPrivate *d, const Char *begin, size_t length, bool isUnicode=true)
Definition qdebug.cpp:198
static QByteArray timeUnit(qint64 num, qint64 den)
Definition qdebug.cpp:345
static bool isPrintable(char32_t ucs4)
Definition qdebug.cpp:192
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint64 key
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint name
GLfloat n
void ** params
const GLubyte * c
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLuint num
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
char Char
QTextStreamManipulator qSetPadChar(QChar ch)
QTextStreamManipulator qSetFieldWidth(int width)
unsigned char uchar
Definition qtypes.h:32
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
static QString quote(const QString &str)
QTextStream out(stdout)
[7]
\inmodule QtCore \reentrant
Definition qchar.h:18
\inmodule QtCore
int indexOfEnumerator(const char *name) const
Finds enumerator name and returns its index; otherwise returns -1.
QMetaEnum enumerator(int index) const
Returns the meta-data for the enumerator with the given index.