qtcassert.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. // Copyright (C) 2016 The Qt Company Ltd.
  2. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
  3. #include "qtcassert.h"
  4. #include <QByteArray>
  5. #include <QDebug>
  6. #include <QMutex>
  7. #include <QTime>
  8. #if defined(Q_OS_UNIX)
  9. #include <stdio.h>
  10. #include <signal.h>
  11. #include <execinfo.h>
  12. #elif defined(_MSC_VER)
  13. #ifdef QTCREATOR_PCH_H
  14. #define CALLBACK WINAPI
  15. #define OUT
  16. #define IN
  17. #endif
  18. #include <Windows.h>
  19. #include <DbgHelp.h>
  20. #endif
  21. namespace Utils {
  22. void dumpBacktrace(int maxdepth)
  23. {
  24. const int ArraySize = 1000;
  25. if (maxdepth < 0 || maxdepth > ArraySize)
  26. maxdepth = ArraySize;
  27. #if defined(Q_OS_UNIX)
  28. void *bt[ArraySize] = {nullptr};
  29. int size = backtrace(bt, maxdepth);
  30. char **lines = backtrace_symbols(bt, size);
  31. for (int i = 0; i < size; ++i)
  32. qDebug() << "0x" + QByteArray::number(quintptr(bt[i]), 16) << lines[i];
  33. free(lines);
  34. #elif defined(_MSC_VER)
  35. DWORD machineType;
  36. #if defined(_M_X64)
  37. machineType = IMAGE_FILE_MACHINE_AMD64;
  38. #elif defined(_M_ARM64)
  39. machineType = IMAGE_FILE_MACHINE_ARM64;
  40. #else
  41. return;
  42. #endif
  43. static QMutex mutex;
  44. mutex.lock();
  45. HANDLE process = GetCurrentProcess();
  46. HANDLE thread = GetCurrentThread();
  47. CONTEXT ctx;
  48. RtlCaptureContext(&ctx);
  49. STACKFRAME64 frame;
  50. memset(&frame, 0, sizeof(STACKFRAME64));
  51. frame.AddrPC.Mode = AddrModeFlat;
  52. frame.AddrStack.Mode = AddrModeFlat;
  53. frame.AddrFrame.Mode = AddrModeFlat;
  54. #if defined(_M_X64)
  55. frame.AddrPC.Offset = ctx.Rip;
  56. frame.AddrStack.Offset = ctx.Rsp;
  57. frame.AddrFrame.Offset = ctx.Rbp;
  58. #elif defined(_M_ARM64)
  59. frame.AddrPC.Offset = ctx.Pc;
  60. frame.AddrStack.Offset = ctx.Sp;
  61. frame.AddrFrame.Offset = ctx.Fp;
  62. #endif
  63. // ignore the first two frames those contain only writeAssertLocation and dumpBacktrace
  64. int depth = -3;
  65. static bool symbolsInitialized = false;
  66. if (!symbolsInitialized) {
  67. SymInitialize(process, NULL, TRUE);
  68. SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
  69. symbolsInitialized = true;
  70. }
  71. while (StackWalk64(machineType,
  72. process,
  73. thread,
  74. &frame,
  75. &ctx,
  76. NULL,
  77. &SymFunctionTableAccess64,
  78. &SymGetModuleBase64,
  79. NULL)) {
  80. if (++depth < 0)
  81. continue;
  82. char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
  83. PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
  84. pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  85. pSymbol->MaxNameLen = MAX_SYM_NAME;
  86. DWORD64 displacement = 0;
  87. if (!SymFromAddr(process, frame.AddrPC.Offset, &displacement, pSymbol))
  88. break;
  89. DWORD symDisplacement = 0;
  90. IMAGEHLP_LINE64 lineInfo;
  91. SymSetOptions(SYMOPT_LOAD_LINES);
  92. QString out = QString(" %1: 0x%2 at %3")
  93. .arg(depth)
  94. .arg(QString::number(pSymbol->Address, 16))
  95. .arg(QString::fromLatin1(&pSymbol->Name[0], pSymbol->NameLen));
  96. if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &symDisplacement, &lineInfo)) {
  97. out.append(QString(" at %3:%4")
  98. .arg(QString::fromLatin1(lineInfo.FileName),
  99. QString::number(lineInfo.LineNumber)));
  100. }
  101. qDebug().noquote() << out;
  102. if (depth == maxdepth)
  103. break;
  104. }
  105. mutex.unlock();
  106. #endif
  107. }
  108. void writeAssertLocation(const char *msg)
  109. {
  110. const QByteArray time = QTime::currentTime().toString(Qt::ISODateWithMs).toLatin1();
  111. static bool goBoom = qEnvironmentVariableIsSet("QTC_FATAL_ASSERTS");
  112. if (goBoom)
  113. qFatal("SOFT ASSERT [%s] made fatal: %s", time.data(), msg);
  114. else
  115. qDebug("SOFT ASSERT [%s]: %s", time.data(), msg);
  116. static int maxdepth = qEnvironmentVariableIntValue("QTC_BACKTRACE_MAXDEPTH");
  117. if (maxdepth != 0)
  118. dumpBacktrace(maxdepth);
  119. }
  120. } // namespace Utils