KDELibs4Support

kdebug.cpp
1 /* This file is part of the KDE libraries
2  Copyright (C) 1997 Matthias Kalle Dalheimer ([email protected])
3  2002 Holger Freyther ([email protected])
4  2007-2011 David Faure ([email protected])
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 #define KDE_EXTENDED_DEBUG_OUTPUT
23 
24 #ifndef QT_NO_CAST_FROM_ASCII
25 #define QT_NO_CAST_FROM_ASCII
26 #endif
27 #ifndef QT_NO_CAST_TO_ASCII
28 #define QT_NO_CAST_TO_ASCII
29 #endif
30 #ifndef KDE3_SUPPORT
31 #define KDE3_SUPPORT
32 #endif
33 
34 #include "kdebug.h"
35 #include <config-io.h>
36 #include <QThreadStorage>
37 
38 #ifdef Q_OS_WIN
39 #include <fcntl.h>
40 #include <windows.h>
41 #ifndef _WIN32_WCE
42 #include <wincon.h>
43 #endif
44 #else
45 #include <unistd.h>
46 #include <stdio.h>
47 #endif
48 
49 #ifdef NDEBUG
50 #undef kDebug
51 #undef kBacktrace
52 #endif
53 
54 #if HAVE_SYS_TIME_H
55 #include <sys/time.h>
56 #endif
57 #if HAVE_TIME_H
58 #include <time.h>
59 #endif
60 
61 #include "kdatetime.h"
62 
63 #include <kmessage.h>
64 #include <klocalizedstring.h>
65 #include <kconfiggroup.h>
66 
67 #include <QFile>
68 #include <QHash>
69 #include <QObject>
70 #include <QChar>
71 #include <QCoreApplication>
72 #include <QUrl>
73 #include <qstandardpaths.h>
74 #include <qlogging.h>
75 
76 #include <stdlib.h> // abort
77 #include <unistd.h> // getpid
78 #include <stdarg.h> // vararg stuff
79 #include <ctype.h> // isprint
80 #include <syslog.h>
81 #include <errno.h>
82 #include <string.h>
83 #include <kconfig.h>
84 
85 #ifdef Q_OS_SOLARIS
86 // For the purposes of KDebug Solaris has a GNU-libc-compatible
87 // backtrace() function. This isn't detected by the CMake checks
88 // normally (check_function_exists fails), but we know it's there.
89 // For better results, we would use walk_context(), but that's
90 // a little more code -- see also the crash handler in kcrash.cpp .
91 #define HAVE_BACKTRACE (1)
92 #endif
93 
94 #if HAVE_BACKTRACE
95 #include <execinfo.h>
96 #ifdef __GNUC__
97 #define HAVE_BACKTRACE_DEMANGLE
98 #include <cxxabi.h>
99 #endif
100 #endif
101 
102 #include "kdebugdbusiface_p.h"
103 #include <QMutex>
104 
105 KDELIBS4SUPPORT_DEPRECATED_EXPORT bool kde_kdebug_enable_dbus_interface = false;
106 
107 class KNoDebugStream: public QIODevice
108 {
109  // Q_OBJECT
110 public:
111  KNoDebugStream()
112  {
113  open(WriteOnly);
114  }
115  bool isSequential() const override
116  {
117  return true;
118  }
119  qint64 readData(char *, qint64) override
120  {
121  return 0; /* eof */
122  }
123  qint64 readLineData(char *, qint64) override
124  {
125  return 0; /* eof */
126  }
127  qint64 writeData(const char *, qint64 len) override
128  {
129  return len;
130  }
131 
132  void setContext(const char *debugFile, int line,
133  const char *funcinfo, const QByteArray &areaName)
134  {
135  context.file = debugFile;
136  context.line = line;
137  context.function = funcinfo;
138  category = areaName; // for storage
139  context.category = category.constData();
140  }
141 protected:
142  QMessageLogContext context;
144 };
145 
146 class KSyslogDebugStream: public KNoDebugStream
147 {
148  // Q_OBJECT
149 public:
150  qint64 writeData(const char *data, qint64 len) override
151  {
152  if (len) {
153  // not using fromRawData because we need a terminating NUL
154  const QByteArray buf(data, len);
155  syslog(m_priority, "%s", buf.constData());
156  }
157  return len;
158  }
159  void setPriority(int priority)
160  {
161  m_priority = priority;
162  }
163 private:
164  int m_priority;
165 };
166 
167 class KFileDebugStream: public KNoDebugStream
168 {
169  // Q_OBJECT
170 public:
171  qint64 writeData(const char *data, qint64 len) override
172  {
173  if (len) {
174  QFile aOutputFile(m_fileName);
176  QByteArray buf = QByteArray::fromRawData(data, len);
177 
178  // Apply QT_MESSAGE_PATTERN
179  const QString formatted = qFormatLogMessage(QtDebugMsg /*hack*/, context, QString::fromUtf8(buf));
180  buf = formatted.toUtf8();
181 
182  aOutputFile.write(buf.trimmed());
183  aOutputFile.putChar('\n');
184  }
185  }
186  return len;
187  }
188  void setFileName(const QString &fn)
189  {
190  m_fileName = fn;
191  }
192 private:
193  QString m_fileName;
194 };
195 
196 class KMessageBoxDebugStream: public KNoDebugStream
197 {
198  // Q_OBJECT
199 public:
200  qint64 writeData(const char *data, qint64 len) override
201  {
202  if (len) {
203  // Since we are in kdecore here, we cannot use KMsgBox
204  QString msg = QString::fromLatin1(data, len);
205  KMessage::message(KMessage::Information, msg, m_caption);
206  }
207  return len;
208  }
209  void setCaption(const QString &h)
210  {
211  m_caption = h;
212  }
213 private:
214  QString m_caption;
215 };
216 
217 class KLineEndStrippingDebugStream: public KNoDebugStream
218 {
219  // Q_OBJECT
220 public:
221  qint64 writeData(const char *data, qint64 len) override
222  {
223  QByteArray buf = QByteArray::fromRawData(data, len);
224  qt_message_output(QtDebugMsg,
225  context,
227  return len;
228  }
229 };
230 
231 struct KDebugPrivate {
232  enum OutputMode {
233  FileOutput = 0,
234  MessageBoxOutput = 1,
235  QtOutput = 2,
236  SyslogOutput = 3,
237  NoOutput = 4,
238  DefaultOutput = QtOutput, // if you change DefaultOutput, also change the defaults in kdebugdialog!
239  Unknown = 5
240  };
241 
242  struct Area {
243  inline Area()
244  {
245  clear();
246  }
247  void clear(OutputMode set = Unknown)
248  {
249  for (int i = 0; i < 4; ++i) {
250  logFileName[i].clear();
251  mode[i] = set;
252  }
253  }
254 
256  QString logFileName[4];
257  OutputMode mode[4];
258  };
259  typedef QHash<unsigned int, Area> Cache;
260 
261  KDebugPrivate()
262  : config(nullptr), kDebugDBusIface(nullptr), m_disableAll(false)
263  {
264  Q_ASSERT(int(QtDebugMsg) == 0);
265  Q_ASSERT(int(QtFatalMsg) == 3);
266 
267  // Create the D-Bus interface if it has not been created yet
268  // But only register to D-Bus if we are in a process with a D-Bus event loop,
269  // otherwise introspection will just hang.
270  // Examples of processes without a D-Bus event loop: kioslaves and the main kdeinit process.
271  //
272  // How to know that we have a real event loop? That's tricky.
273  // We could delay registration in kDebugDBusIface with a QTimer, but
274  // it would still get triggered by kioslaves that use enterLoop/exitLoop
275  // to run kio jobs synchronously.
276  //
277  // Solution: we have a bool that is set by KApplication
278  // (kioslaves should use QCoreApplication but not KApplication).
279  if (kde_kdebug_enable_dbus_interface) {
280  kDebugDBusIface = new KDebugDBusIface;
281  }
282 
283  for (int i = 0; i < 8; i++) {
284  m_nullOutputYesNoCache[i] = -1;
285  }
286 
287  }
288 
289  ~KDebugPrivate()
290  {
291  delete config;
292  delete kDebugDBusIface;
293  }
294 
295  void loadAreaNames()
296  {
297  // Don't clear the cache here, that would lose previously registered dynamic areas
298  //cache.clear();
299 
300  Area &areaData = cache[0];
301  areaData.clear();
302 
303  areaData.name = qApp ? QCoreApplication::applicationName().toUtf8() : QByteArray("unnamed app");
304  //qDebug() << "loadAreaNames: area 0 has name" << areaData.name;
305 
306  for (int i = 0; i < 8; i++) {
307  m_nullOutputYesNoCache[i] = -1;
308  }
309 
311  if (!QFile::exists(filename)) {
312  return;
313  }
314  QFile file(filename);
315  if (!file.open(QIODevice::ReadOnly)) {
316  qWarning("Couldn't open %s", filename.toLocal8Bit().constData());
317  file.close();
318  return;
319  }
320 
321  uint lineNumber = 0;
322 
323  while (!file.atEnd()) {
324  const QByteArray line = file.readLine().trimmed();
325  ++lineNumber;
326  if (line.isEmpty()) {
327  continue;
328  }
329 
330  int i = 0;
331  unsigned char ch = line[i];
332 
333  if (ch == '#') {
334  continue; // We have an eof, a comment or an empty line
335  }
336 
337  if (ch < '0' || ch > '9') {
338  qWarning("Syntax error parsing '%s': no number (line %u)", qPrintable(filename), lineNumber);
339  continue;
340  }
341 
342  do {
343  ch = line[++i];
344  } while (ch >= '0' && ch <= '9' && i < line.length());
345 
346  unsigned int number = line.left(i).toUInt();
347 
348  while (i < line.length() && line[i] <= ' ') {
349  i++;
350  }
351 
352  Area areaData;
353  areaData.name = line.mid(i);
354  cache.insert(number, areaData);
355  }
356  file.close();
357  }
358 
359  inline int level(QtMsgType type)
360  {
361  return int(type) - int(QtDebugMsg);
362  }
363 
364  QString groupNameForArea(unsigned int area) const
365  {
366  QString groupName = QString::number(area);
367  if (area == 0 || !config->hasGroup(groupName)) {
368  groupName = QString::fromLocal8Bit(cache.value(area).name);
369  }
370  return groupName;
371  }
372 
373  OutputMode areaOutputMode(QtMsgType type, unsigned int area, bool enableByDefault)
374  {
375  if (!configObject()) {
376  return QtOutput;
377  }
378 
379  QString key;
380  switch (type) {
381  case QtDebugMsg:
382  key = QLatin1String("InfoOutput");
383  if (m_disableAll) {
384  return NoOutput;
385  }
386  break;
387  case QtWarningMsg:
388  key = QLatin1String("WarnOutput");
389  break;
390  case QtFatalMsg:
391  key = QLatin1String("FatalOutput");
392  break;
393  case QtCriticalMsg:
394  default:
395  /* Programmer error, use "Error" as default */
396  key = QLatin1String("ErrorOutput");
397  break;
398  }
399 
400  const KConfigGroup cg(config, groupNameForArea(area));
401  const int mode = cg.readEntry(key, int(enableByDefault ? DefaultOutput : NoOutput));
402  return OutputMode(mode);
403  }
404 
405  QString logFileName(QtMsgType type, unsigned int area)
406  {
407  if (!configObject()) {
408  return QString();
409  }
410 
411  const char *aKey;
412  switch (type) {
413  case QtDebugMsg:
414  aKey = "InfoFilename";
415  break;
416  case QtWarningMsg:
417  aKey = "WarnFilename";
418  break;
419  case QtFatalMsg:
420  aKey = "FatalFilename";
421  break;
422  case QtCriticalMsg:
423  default:
424  aKey = "ErrorFilename";
425  break;
426  }
427 
428  KConfigGroup cg(config, groupNameForArea(area));
429  return cg.readPathEntry(aKey, QLatin1String("kdebug.dbg"));
430  }
431 
432  KConfig *configObject()
433  {
434  if (!config) {
435  config = new KConfig(QLatin1String("kdebugrc"), KConfig::NoGlobals);
436  m_disableAll = config->group(QString()).readEntry("DisableAll", false);
437  }
438  return config;
439  }
440 
441  Cache::Iterator areaData(QtMsgType type, unsigned int num, bool enableByDefault = true)
442  {
443  if (!cache.contains(0)) {
444  //qDebug() << "cache size=" << cache.count() << "loading area names";
445  loadAreaNames(); // fills 'cache'
446  Q_ASSERT(cache.contains(0));
447  }
448 
449  Cache::Iterator it = cache.find(num);
450  if (it == cache.end()) {
451  // unknown area
452  Q_ASSERT(cache.contains(0));
453  it = cache.find(0);
454  num = 0;
455  }
456 
457  if (num == 0) { // area 0 is special, it becomes the named area "appname"
458  static bool s_firstDebugFromApplication = true;
459  if (s_firstDebugFromApplication && !m_disableAll) {
460  s_firstDebugFromApplication = false;
461  //qDebug() << "First debug output from" << it->name << "writing out with default" << enableByDefault;
462  writeGroupForNamedArea(it->name, enableByDefault);
463  }
464  }
465 
466  const int lev = level(type);
467  //qDebug() << "in cache for" << num << ":" << it->mode[lev];
468  if (it->mode[lev] == Unknown) {
469  it->mode[lev] = areaOutputMode(type, num, enableByDefault);
470  }
471  if (it->mode[lev] == FileOutput && it->logFileName[lev].isEmpty()) {
472  it->logFileName[lev] = logFileName(type, num);
473  }
474 
475  Q_ASSERT(it->mode[lev] != Unknown);
476 
477  return it;
478  }
479 
480  QDebug setupFileWriter(const QString &fileName)
481  {
482  if (!filewriter.hasLocalData()) {
483  filewriter.setLocalData(new KFileDebugStream);
484  }
485  filewriter.localData()->setFileName(fileName);
486  QDebug result(filewriter.localData());
487  return result;
488  }
489 
490  QDebug setupMessageBoxWriter(QtMsgType type, const QByteArray &areaName)
491  {
492  if (!messageboxwriter.hasLocalData()) {
493  messageboxwriter.setLocalData(new KMessageBoxDebugStream);
494  }
495  QDebug result(messageboxwriter.localData());
496  QByteArray header;
497 
498  switch (type) {
499  case QtDebugMsg:
500  header = "Info";
501  break;
502  case QtWarningMsg:
503  header = "Warning";
504  break;
505  case QtFatalMsg:
506  header = "Fatal Error";
507  break;
508  case QtCriticalMsg:
509  default:
510  header = "Error";
511  break;
512  }
513 
514  if (!areaName.isEmpty()) {
515  header += " (";
516  header += areaName;
517  header += ')';
518  }
519  messageboxwriter.localData()->setCaption(QString::fromLatin1(header));
520  return result;
521  }
522 
523  QDebug setupSyslogWriter(QtMsgType type)
524  {
525  if (!syslogwriter.hasLocalData()) {
526  syslogwriter.setLocalData(new KSyslogDebugStream);
527  }
528  QDebug result(syslogwriter.localData());
529  int level = 0;
530 
531  switch (type) {
532  case QtDebugMsg:
533  level = LOG_INFO;
534  break;
535  case QtWarningMsg:
536  level = LOG_WARNING;
537  break;
538  case QtFatalMsg:
539  level = LOG_CRIT;
540  break;
541  case QtCriticalMsg:
542  default:
543  level = LOG_ERR;
544  break;
545  }
546  syslogwriter.localData()->setPriority(level);
547  return result;
548  }
549 
550  QDebug setupQtWriter(QtMsgType type)
551  {
552  if (type == QtWarningMsg) {
553  // KDE warnings are not the same thing as Qt warnings
554  // in Qt, warnings indicate bad code, which must be corrected before the release
555  // in KDE, it's just something that everyone sees (including users)
556  type = QtDebugMsg;
557  }
558  if (type != QtDebugMsg) {
559  return QDebug(type);
560  }
561  return QDebug(&lineendstrippingwriter);
562  }
563 
564  QDebug printHeader(QDebug s)
565  {
566 #ifdef KDE_EXTENDED_DEBUG_OUTPUT
567  static int printTimeStamp = qEnvironmentVariableIntValue("KDE_DEBUG_TIMESTAMP");
568  //s = s.nospace();
569  if (printTimeStamp > 0) {
570  if (printTimeStamp >= 2) {
571  // the extended print: 17:03:24.123
572  const QString sformat = QString::fromLatin1("hh:mm:ss.zzz");
573  s << qPrintable(QDateTime::currentDateTime().time().toString(sformat));
574  } else {
575  // the default print: 17:03:24
576  s << qPrintable(QDateTime::currentDateTime().time().toString());
577  }
578  //s << ' ';
579  }
580 
581  if (m_indentString.hasLocalData()) {
582  s.setAutoInsertSpaces(false);
583  s << m_indentString.localData()->toLatin1().constData();
584  s.setAutoInsertSpaces(true);
585  }
586 
587 #if 0 // This is in Qt now, see %{function} in QT_MESSAGE_PATTERN (qlogging.cpp). Only the coloring is missing (TODO Qt-5.1)
588  if (funcinfo && printMethodName) {
589  if (colored) {
590  if (type <= QtDebugMsg) {
591  s << "\033[0;34m"; //blue
592  } else {
593  s << "\033[0;31m"; //red
594  }
595  }
596 # ifdef Q_CC_GNU
597  // strip the function info down to the base function name
598  // note that this throws away the template definitions,
599  // the parameter types (overloads) and any const/volatile qualifiers
600  QByteArray info = funcinfo;
601  int pos = info.indexOf('(');
602  Q_ASSERT_X(pos != -1, "kDebug",
603  "Bug in kDebug(): I don't know how to parse this function name");
604  while (info.at(pos - 1) == ' ')
605  // that '(' we matched was actually the opening of a function-pointer
606  {
607  pos = info.indexOf('(', pos + 1);
608  }
609 
610  info.truncate(pos);
611  // gcc 4.1.2 don't put a space between the return type and
612  // the function name if the function is in an anonymous namespace
613  int index = 1;
614  forever {
615  index = info.indexOf("<unnamed>::", index);
616  if (index == -1)
617  {
618  break;
619  }
620 
621  if (info.at(index - 1) != ':')
622  {
623  info.insert(index, ' ');
624  }
625 
626  index += strlen("<unnamed>::");
627  }
628  pos = info.lastIndexOf(' ');
629  if (pos != -1) {
630  int startoftemplate = info.lastIndexOf('<');
631  if (startoftemplate != -1 && pos > startoftemplate &&
632  pos < info.lastIndexOf(">::"))
633  // we matched a space inside this function's template definition
634  {
635  pos = info.lastIndexOf(' ', startoftemplate);
636  }
637  }
638 
639  if (pos + 1 == info.length())
640  // something went wrong, so gracefully bail out
641  {
642  s << " " << funcinfo;
643  } else {
644  s << " " << info.constData() + pos + 1;
645  }
646 # else
647  s << " " << funcinfo;
648 # endif
649  if (colored) {
650  s << "\033[0m";
651  }
652  }
653 
654  s << ":";
655  s.space();
656 #endif
657 #else // KDE_EXTENDED_DEBUG_OUTPUT
658  Q_UNUSED(funcinfo);
659  if (!areaName.isEmpty()) {
660  s.nospace();
661  s << areaName.constData() << ':';
662  s.space();
663  }
664 #endif
665  return s;
666  }
667 
668  QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line,
669  const char *funcinfo)
670  {
671  Cache::Iterator it = areaData(type, area);
672  OutputMode mode = it->mode[level(type)];
673  Q_ASSERT(mode != Unknown);
674  QString file = it->logFileName[level(type)];
675 
676  QByteArray areaName = it->name;
677  //if (areaName.isEmpty())
678  // areaName = cache.value(0).name;
679 
680  QDebug s(&devnull);
681  switch (mode) {
682  case FileOutput:
683  s = setupFileWriter(file);
684  filewriter.localData()->setContext(debugFile, line, funcinfo, areaName);
685  break;
686  case MessageBoxOutput:
687  s = setupMessageBoxWriter(type, areaName);
688  break;
689  case SyslogOutput:
690  s = setupSyslogWriter(type);
691  break;
692  case NoOutput:
693  s = QDebug(&devnull);
694  return s; //no need to take the time to "print header" if we don't want to output anyway
695  break;
696  case Unknown: // should not happen
697  default: // QtOutput
698  lineendstrippingwriter.setContext(debugFile, line, funcinfo, areaName);
699  s = setupQtWriter(type);
700  break;
701  }
702 
703  return printHeader(s);
704  }
705 
706  void writeGroupForNamedArea(const QByteArray &areaName, bool enabled)
707  {
708  // Ensure that this area name appears in kdebugrc, so that users (via kdebugdialog)
709  // can turn it off.
710  KConfig *cfgObj = configObject();
711  if (cfgObj) {
712  KConfigGroup cg(cfgObj, QString::fromUtf8(areaName));
713  const QString key = QString::fromLatin1("InfoOutput");
714  if (!cg.hasKey(key)) {
715  cg.writeEntry(key, int(enabled ? KDebugPrivate::QtOutput : KDebugPrivate::NoOutput));
716  cg.sync();
717  }
718  }
719  }
720 
721  QMutex mutex;
722  KConfig *config;
723  KDebugDBusIface *kDebugDBusIface;
724  Cache cache;
725  bool m_disableAll;
726  int m_nullOutputYesNoCache[8];
727 
728  KNoDebugStream devnull;
729  QThreadStorage<QString *> m_indentString;
733  KLineEndStrippingDebugStream lineendstrippingwriter;
734 };
735 
736 Q_GLOBAL_STATIC(KDebugPrivate, kDebug_data)
737 
738 #if HAVE_BACKTRACE
739 static QString maybeDemangledName(char *name)
740 {
741 #ifdef HAVE_BACKTRACE_DEMANGLE
742  const int len = strlen(name);
743  QByteArray in = QByteArray::fromRawData(name, len);
744  const int mangledNameStart = in.indexOf("(_");
745  if (mangledNameStart >= 0) {
746  const int mangledNameEnd = in.indexOf('+', mangledNameStart + 2);
747  if (mangledNameEnd >= 0) {
748  int status;
749  // if we forget about this line and the one that undoes its effect we don't change the
750  // internal data of the QByteArray::fromRawData() ;)
751  name[mangledNameEnd] = 0;
752  char *demangled = abi::__cxa_demangle(name + mangledNameStart + 1, nullptr, nullptr, &status);
753  name[mangledNameEnd] = '+';
754  if (demangled) {
755  QString ret = QString::fromLatin1(name, mangledNameStart + 1) +
756  QString::fromLatin1(demangled) +
757  QString::fromLatin1(name + mangledNameEnd, len - mangledNameEnd);
758  free(demangled);
759  return ret;
760  }
761  }
762  }
763 #endif
764  return QString::fromLatin1(name);
765 }
766 #endif
767 
769 {
770  QString s;
771 #if HAVE_BACKTRACE
772  void *trace[256];
773  int n = backtrace(trace, 256);
774  if (!n) {
775  return s;
776  }
777  char **strings = backtrace_symbols(trace, n);
778 
779  if (levels != -1) {
780  n = qMin(n, levels);
781  }
782  s = QLatin1String("[\n");
783 
784  for (int i = 0; i < n; ++i)
785  s += QString::number(i) + QLatin1String(": ") +
786  maybeDemangledName(strings[i]) + QLatin1Char('\n');
787  s += QLatin1String("]\n");
788  if (strings) {
789  free(strings);
790  }
791 #endif
792  return s;
793 }
794 
796 {
797  return QDebug(&kDebug_data()->devnull);
798 }
799 
800 QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
801 {
802  if (kDebug_data.isDestroyed()) {
803  // we don't know what to return now...
804  qCritical().nospace() << "kDebugStream called after destruction (from "
805  << (funcinfo ? funcinfo : "")
806  << (file ? " file " : " unknown file")
807  << (file ? file : "")
808  << " line " << line << ")";
809  return QDebug(level);
810  }
811 
812  QMutexLocker locker(&kDebug_data()->mutex);
813  return kDebug_data()->stream(level, area, file, line, funcinfo);
814 }
815 
816 QDebug perror(QDebug s, KDebugTag)
817 {
818  return s << QString::fromLocal8Bit(strerror(errno));
819 }
820 
821 QDebug operator<<(QDebug s, const KDateTime &time)
822 {
823  if (time.isDateOnly()) {
824  s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")";
825  } else {
826  s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")";
827  }
828  return s.space();
829 }
830 
831 void kClearDebugConfig()
832 {
833  if (!kDebug_data) {
834  return;
835  }
836  KDebugPrivate *d = kDebug_data;
837  QMutexLocker locker(&d->mutex);
838  delete d->config;
839  d->config = nullptr;
840 
841  KDebugPrivate::Cache::Iterator it = d->cache.begin(),
842  end = d->cache.end();
843  for (; it != end; ++it) {
844  it->clear();
845  }
846 
847  for (int i = 0; i < 8; i++) {
848  d->m_nullOutputYesNoCache[i] = -1;
849  }
850 }
851 
852 // static
853 bool KDebug::hasNullOutput(QtMsgType type,
854  bool condition,
855  int area,
856  bool enableByDefault)
857 {
858  if (!condition) {
859  return true;
860  }
861  if (kDebug_data.isDestroyed()) {
862  // kDebugStream() will generate a warning anyway, so we don't.
863  return false;
864  }
865  KDebugPrivate *const d = kDebug_data;
866  QMutexLocker locker(&d->mutex);
867 
868  if (type == QtDebugMsg) {
869  int *entries = d->m_nullOutputYesNoCache;
870  for (int i = 0; i < 8; i += 2) {
871  if (entries[i] == area) {
872  return entries[i + 1];
873  }
874  }
875  }
876 
877  KDebugPrivate::Cache::Iterator it = d->areaData(type, area, enableByDefault);
878  const bool ret = it->mode[d->level(type)] == KDebugPrivate::NoOutput;
879 
880  // cache result for next time...
881  if (type == QtDebugMsg) {
882  int *entries = d->m_nullOutputYesNoCache;
883  int idx = (qrand() % 4) * 2;
884  entries[idx] = area;
885  entries[idx + 1] = ret;
886  }
887 
888  return ret;
889 }
890 
891 int KDebug::registerArea(const QByteArray &areaName, bool enabled)
892 {
893  // TODO for optimization: static int s_lastAreaNumber = 1;
894  KDebugPrivate *d = kDebug_data;
895  QMutexLocker locker(&d->mutex);
896  int areaNumber = 1;
897  while (d->cache.contains(areaNumber)) {
898  ++areaNumber;
899  }
900  KDebugPrivate::Area areaData;
901  areaData.name = areaName;
902  //qDebug() << "Assigning area number" << areaNumber << "for name" << areaName;
903  d->cache.insert(areaNumber, areaData);
904  d->writeGroupForNamedArea(areaName, enabled);
905  return areaNumber;
906 }
907 
908 #ifndef KDE_NO_DEBUG_OUTPUT
909 
910 class Q_DECL_HIDDEN KDebug::Block::Private
911 {
912 public:
913  QByteArray m_label;
914 };
915 
916 KDebug::Block::Block(const char *label, int area)
917  : m_area(area), d(nullptr)
918 {
919  if (hasNullOutputQtDebugMsg(area)) {
920  d = nullptr; // remember, for the dtor
921  } else {
922  d = new Private;
923  d->m_label = label;
924  m_startTime.start();
925  kDebug(area) << "BEGIN:" << label;
926 
927  // The indent string is per thread
928  QThreadStorage<QString *> &indentString = kDebug_data()->m_indentString;
929  if (!indentString.hasLocalData()) {
930  indentString.setLocalData(new QString);
931  }
932  *(indentString.localData()) += QLatin1String(" ");
933  }
934 }
935 
936 KDebug::Block::~Block()
937 {
938  if (d) {
939  const double duration = m_startTime.elapsed() / 1000.0;
940  QThreadStorage<QString *> &indentString = kDebug_data()->m_indentString;
941  indentString.localData()->chop(2);
942 
943  // Print timing information, and a special message (DELAY) if the method took longer than 5s
944  if (duration < 5.0) {
945  kDebug(m_area)
946  << "END__:"
947  << d->m_label.constData()
948  << qPrintable(QString::fromLatin1("[Took: %3s]").arg(QString::number(duration, 'g', 2)));
949  } else {
950  kDebug(m_area)
951  << "END__:"
952  << d->m_label.constData()
953  << qPrintable(QString::fromLatin1("[DELAY Took (quite long) %3s]").arg(QString::number(duration, 'g', 2)));
954  }
955  delete d;
956  }
957 }
958 
959 #endif
@ ISODate
ISO 8601 format, i.e.
Definition: kdatetime.h:399
bool isDateOnly() const
Returns whether the instance represents a date/time or a date-only value.
Definition: kdatetime.cpp:877
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
void setLocalData(T data)
QString number(int n, int base)
QString fromUtf8(const char *str, int size)
QByteArray fromRawData(const char *data, int size)
void setAutoInsertSpaces(bool b)
int indexOf(char ch, int from) const const
Type type(const QSqlDatabase &db)
QDateTime currentDateTime()
QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
Definition: kdebug.cpp:800
KCOREADDONS_EXPORT void message(KMessage::MessageType messageType, const QString &text, const QString &caption=QString())
QDebug & nospace()
QStringView level(QStringView ifopt)
virtual bool open(QIODevice::OpenMode mode)
QDebug & space()
QByteArray trimmed() const const
static KDELIBS4SUPPORT_DEPRECATED_EXPORT_NOISE bool hasNullOutput(QtMsgType type, bool condition, int area, bool enableByDefault)
Definition: kdebug.cpp:853
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
bool exists() const const
uint toUInt(bool *ok, int base) const const
char at(int i) const const
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
int lastIndexOf(char ch, int from) const const
static bool hasNullOutputQtDebugMsg(int area=KDE_DEFAULT_DEBUG_AREA)
Definition: kdebug.h:334
bool hasLocalData() const const
QString fromLocal8Bit(const char *str, int size)
A class representing a date and time with an associated time zone.
Definition: kdatetime.h:148
QByteArray mid(int pos, int len) const const
QByteArray toUtf8() const const
virtual bool isSequential() const const
Q_SCRIPTABLE CaptureState status()
Get the current networking status If the result is Unknown, the backend may be unconfigured or otherw...
Definition: networking.cpp:40
qint64 elapsed() const const
KSharedConfigPtr config()
Returns the general config object.
Definition: kglobal.cpp:102
static KDELIBS4SUPPORT_DEPRECATED_EXPORT_NOISE int registerArea(const QByteArray &areaName, bool enabled=true)
Definition: kdebug.cpp:891
virtual qint64 readLineData(char *data, qint64 maxSize)
QString label(StandardShortcut id)
QAction * clear(const QObject *recvr, const char *slot, QObject *parent)
QByteArray left(int len) const const
bool isEmpty() const const
const char * constData() const const
QString fromLatin1(const char *str, int size)
const char * name(StandardAction id)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
QString toString(const QString &format) const
Returns the date/time as a string.
Definition: kdatetime.cpp:1482
@ QtTextDate
Same format as Qt::TextDate (i.e.
Definition: kdatetime.h:423
int length() const const
QString kRealBacktrace(int levels)
Definition: kdebug.cpp:768
void truncate(int pos)
virtual qint64 writeData(const char *data, qint64 maxSize)=0
QDebug kDebugDevNull()
Definition: kdebug.cpp:795
Definition: kdebug.h:300
virtual qint64 readData(char *data, qint64 maxSize)=0
Category category(StandardShortcut id)
const QList< QKeySequence > & end()
char * toString(const EngineQuery &query)
QByteArray & insert(int i, char ch)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Dec 8 2023 03:58:25 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.